3

I'm learning Clojure. I wrote this code to recursively walk a directory.

(tree-seq #(.isDirectory %1) #(.listFiles %1) (File. "/my-directory"))

Why can't I use .isDirectory as a first-class function in Clojure? Is there a better way to rewrite this code?

woodings
  • 7,503
  • 5
  • 34
  • 52

4 Answers4

7

Joost is spot on about Java methods not being first class functions.

As an approach to dealing with this, I usually like to wrap Java functions in a Clojure function (or find a library that does this already), then it is easy to use them in an idiomatic first-class way:

(defn directory? [^java.io.File file]
  (.isDirectory file))

(defn list-files [^java.io.File file]
  (.listFiles %1))

(tree-seq directory? list-files (File. "/my-directory"))

This is a few more lines of code, but has the following advantages:

  • You can add type hints in your functions to avoid reflection (as above)
  • The final code is cleaner and more idiomatic
  • You have abstracted away from the underlying Java interop
mikera
  • 105,238
  • 25
  • 256
  • 415
6

Java methods aren't clojure functions because you can't call a method on its own; you have to call a method on an object, and it has to be an object of the type that the method expects. In other words, in java, a method cannot be fully separated from its defining class (at least not efficiently).

An alternative to #(.foo %) would be (memfn foo), which hardly anyone uses anymore after #(...) was introduced.

Joost Diepenmaat
  • 17,633
  • 3
  • 44
  • 53
1

You could have a look at the sourcecode of file-seq (which uses a tree-seq) to see how it works.

By the way: your code works perfectly well for me. I just have to use java.io.File instead of File in the REPL so it knows the Java class.

Maurits Rijk
  • 9,789
  • 2
  • 36
  • 53
1

You've already been given the correct answers, but just to add a bit more Clojure idiomatic code, I'd also use

#(.foo %)

as Joost Diepenmaat did (yet I believed it might've been overlooked).

I would also suggest reading Listing files in a directory in Clojure.

Community
  • 1
  • 1
Jacek Laskowski
  • 72,696
  • 27
  • 242
  • 420