This package does not yet contain any implementation. Just an idea.
The main part of the idea is to have code that runs on Clojure version 1.10 use
java.nio.file.Path
and friends, and not java.io.File
.
More specifically, I'd propose to have a package that provides every function
currently provided by
clojure.java.io
but using Java's nio
package, not the io
package.
This could be done with a brand new clojure.java.nio
package, as a plug-in
replacement; or by directly re-implementing clojure.java.io
to change the
underlying implementation; or as a third-party package, not specifically part
of the Clojure ecosystem.
The java.nio
package provides far more than you can get from the existing
functions in clojure.java.io
, of course. It would be nice to expose more
as part of the standard Clojure language. But providing replacements for
the clojure.java.io
functions is a good start.
There should be a standard, simple, idiomatically Clojure way to access the file system.
The old java.io.File
class is bad and should be avoided when possible.
Clojure provides a standard library with polymorphic I/O utility functions, clojure.java.io.
That library began life as a clojure.contrib
package, and was "promoted"
to being part of the Clojure project in May of 2010, for Clojure 1.2.
At the time clojure.java.io
became a thing, Java 6 was quite mature (released
December 2006) and Java 7 was still over a year away.
Java contains some much-reviled classes for common pieces of functionality,
including java.util.Date
and java.io.File
. As of Java 6, there was no
alternative to using java.io.File
(without resorting to JNI).
It seems that the first few versions of Clojure did not specifically document a required version of Java, but since Clojure uses java.util.concurrent, it seems reasonable to assume that Java 5 was required.
With the reducers library added in Clojure 1.5 (March 2013), Java 6 was required. Java 6 became a requirement upon the release of Clojure 1.6 in March of 2014 --the same month Java 8 was released.
Numerous Clojure applications were built on top of Java 6. When there were new Clojure versions, it was desired that Clojure applications should be able to upgrade their version of Clojure, without necessarily having to upgrade their JVM. Java 6 remained the requirement for Clojure 1.6 - 1.8. This meant, of course, that the Clojure language could not use any features that were not present in Java 6.
As of the release of Clojure 1.9, the requirement remains Java 1.6; however, Java 1.8+ is "recommended".
The latest development release as of this writing, 1.10.0-alpha7, requires Java 1.8+.
Of course, it is possible to write code in a way that runs on Java 6, but takes advantage of features of Java 8, when present. The code can do a runtime check, and invoke one set of functionality or the other, depending on its environment.
In fact, the fs
project (see below) did just that. Initially,
my idea was to use techniques like the
one in fs
to develop a "unified" file I/O, that would use, and would be based
on, java.nio
as much as possible, by providing implementations of nio
functionality using java.io.File
.
But if Clojure 1.10 is going to require Java 8 anyway, any Clojure 1.10 library
should be able to feel free to assume that java.nio
is present.
The java.nio
package is better.
There are no Clojure-specific reasons why it's better, so to understand why it's better, one can simply read documents about why it's better in Java programs.
I would not inline that information here, but I could perhaps find some existing
documents and link to them. (TODO). Until then, understanding why java.nio
is
better than java.io
is left as an exercise for the reader...
Again, this has nothing in particular to do with Clojure, nor with file I/O. In general, when a group of programmers writing in a given language need to access the same functionality, there are many advantages to having them use the same library, rather than everyone inventing their own interfaces.
As above, I could find and link to some essays about why standard libraries are so important, but for now, I'll take it as a given that they are.
It has been correctly pointed out that Clojure never imposed a maximum version of Java. If a Clojure programmer wanted to use functionality that was new in Java 8, they could certainly do so. They could require Java 8 for their project. The fact that the Clojure language did not require it does not prevent a specific, non-language library or application from doing so.
In addition to all the usual reasons why it's easier and better to use a standard library rather than rolling your own, newer versions of Java introduced an additional complication: variadic arguments.
For example, java.nio.file.Paths
contains a static method,
public static Path get(String first, String... more)
This has one required String argument, and any number of additional String
arguments. It could, for instance, be called with either of the following:
Path newPath = Paths.get("/usr", "bin", "java");
or
Path newPath = Paths.get("/usr/bin", "java");
In the JVM, the "real" method signature is Path get(String, String[])
.
The Java compiler packages the extra arguments into a String[]
, and
passes that array to the method. It has to be a String[]
. It can't
be a generic array of Objects, let alone any sort of Collection.
This can be done from Clojure, of course, but it's a little low-level.
From Java, the variadic method can also be called with zero additional
arguments:
Path newPath = Paths.get(filepath);
Since the JVM supports polymorphism via argument lists, it could
define a method that simply takes a String. But (at least in this case),
it doesn't. The above code also is translated by the complier into an
invocation that passes a String[]
, albeit an empty one.
In order to call these methods from Clojure, you need to create String arrays. Again, hardly the end of the world once you understand how to do it (and why it's necessary), but it's definitely ugly, and definitely a stumbling block for people who aren't highly experienced with Clojure.
In searching GitHub to see to what extent all this has already been done, I found another person addressing the same question: Why wrap java.nio?
Clojure support for java.nio.file
has been requested:
Support java.nio.file.Path in clojure.java.io