How can I use java methods as a functions arguments in Clojure?
For example, I want to make a functions composition:
user> (Integer. (str \9))
9
user> ((comp Integer. str) \9)
CompilerException java.lang.ClassNotFoundException: Integer., compiling:(NO_SOURCE_PATH:1:2)
That does not work.
memfn doesn't help also:
user> (map (comp (memfn Integer.) str) "891")
IllegalArgumentException No matching method found: Integer. for class java.lang.String clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:53)
Any ideas?
Related questions (that, though, do not give the right answer to the question):
Using interop constructor in map function(Clojure)
Why does Clojure say "no matching method" for an illegal argument?
How do I use Clojure memfn with a Java constructor?
How to get an Fn which calls a Java method? (has a nice explanation in the answers)
(Note: it seems to be that the answer suggested by dave, using of an anonymous function as a wrapper, is the best solution)
Unlike Clojure functions, Java methods weren't designed to be first class. When you use Java inter-op in Clojure, you're literally working with Java methods, so you don't get the added benefits that were implemented for Clojure functions. For more info, see the comments below.
As a workaround to use Java methods as arguments, you can wrap them in an anonymous function, like this, effectively making them Clojure functions:
((comp #(Integer. %) str) \9)
Related
I am not able to find any documentation (or blogs) regarding this. Is it possible to call package protected methods from clojure? We have a huge java code base and I was thinking if its possible to start developing some clojure libraries around them.
I tried making the namespace same as that of the package in java but the method (static) was not found.
Here's a quick code example:
(def a (ref 0))
(def klass (class a))
(def m (.getDeclaredMethod klass "currentVal" (into-array Class [])))
(.setAccessible m true)
(.invoke m a (into-array []))
You should probably have a look at the reflect API
Although, I would recommend only using the publicly declared fields and methods from your old java code base.
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?
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
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.
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.
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.
In Clojure, how to use a java Class that is stored in a variable?
How should I fix the following code?
(def a java.lang.String)
(new a "1"); CompilerException java.lang.IllegalArgumentException: Unable to resolve classname: a
And why this one works fine?
(def a str)
(a "1")
The most elegant solution is to write construct that does the same as new but is able to receive a class dynamically:
(defn construct [klass & args]
(clojure.lang.Reflector/invokeConstructor klass (into-array Object args)))
(def a HashSet)
(construct a '(1 2 3)); It works!!!
This solution overcomes the limitation of #mikera's answer (see comments).
Special Thanks to #MichaĆ Marczyk that made me aware of invokeConstructor answering another question of mine: Clojure: how to create a record inside a function?.
Another option is to store the call to the constructor as an anonymous function. In our case:
(def a #(String. %1))
(a "111"); "111"
When you define a in this way, you get a var containing a java.lang.Class
(def a java.lang.String)
(type a)
=> java.lang.Class
You then have 2 options:
A: Construct the new instance dynamically by finding the Java constructor using the reflection API. Note that as Yehonathan points out you need to use the exact class defined in the constructor signature (a subclass won't work as it won't find the correct signature):
(defn construct [klass & args]
(.newInstance
(.getConstructor klass (into-array java.lang.Class (map type args)))
(object-array args)))
(construct a "Foobar!")
=> "Foobar!"
B: Construct using Clojure's Java interop, which will require an eval:
(defn new-class [klass & args]
(eval `(new ~klass ~#args)))
(new-class a "Hello!")
=> "Hello!"
Note that method A is considerably faster (about 60x faster on my machine), I think mainly because it avoids the overhead of invoking the Clojure compiler for each eval statement.
The problem is that Clojure implements Java interop using a number of special forms:
user=> (doc new)
-------------------------
new
Special Form
Please see http://clojure.org/special_forms#new
nil
this basically means the "normal" Clojure syntax is altered to allow for handier constructs when calling Java. As a naive reflection solution to your dynamic Java needs, you can leverage eval:
user=> (def a String) ; java.lang package is implicitly imported
#'user/a
user=> `(new ~a "test") ; syntax quote to create the correct form
(new java.lang.String "test")
user=> (eval `(new ~a "test")) ; eval to execute
"test"
The same strategy works with all the other interop special forms, like method invocation.
EDIT: look also at the answer from #mikera for a more performing alternative via the Java reflection API.
I would like to provide some methods for a clojure protocol starting with the : character. Is there any way to override this in Clojure?
Don't think so. Clojure keywords are implemented in the reader and I don't think there's any way of overriding that behavior.
When you use a keyword as a function, that is equivalent to (get arg :keyword). You can extend what that does by implementing ILookup in your protocol.
Joost.
Sounds like a bad idea: colons are reserved for keywords so even if you could do this I think it would make for some confusing code.
You can of course, put a function inside a record mapped by a keyword:
(defrecord Foo [])
(def foo (Foo. nil {:method (fn [a b] (* a b))}))
((:method foo) 7 10)
=> 70
I've found this to be a useful trick sometimes......
Apparently, you can't call apply with a record constructor:
(defrecord Foo. [id field])
(apply Foo. my-list)
fails at read time because it is not expecting Foo. in that place.
The only obvious workaround I could think of was to add a factory function:
(make-foo [id field] (Foo. id field))
which can be apply'ed of course.
Am I missing anything? I'd expect this from C#/Java but just thought it was a bit disappointing in Clojure...
Circling back on this post-1.3....
In Clojure 1.3, defrecord creates two generated constructor functions. Given:
(defrecord Person [first last])
this will create a positional constructor function ->Person:
(->Person "alex" "miller")
and a map constructor function map->Person:
(map->Person {:first "string"})
Because this is a map, all keys are optional and take on a nil value in the constructed object.
You should require/use these functions from the ns where you declare the record, but you do not need to import the record class as you would when using the Java class constructor.
More details:
http://dev.clojure.org/display/design/defrecord+improvements
http://groups.google.com/group/clojure/browse_thread/thread/ce22faf3657ca00a/beb75e61ae0d3f53
Foo. is a Java class constructor so it has typical Java interop constraints with how you call it. Creating a constructor function is a common solution (it also means you don't have to import the Foo when in a different namespace).
The problem is known and there is lots of talk about it on the Clojure mailing list. More support will probably be added in future Clojure versions.
For now you have to use your own functions or use https://github.com/david-mcneil/defrecord2 which supports some features like:
print in an eval'able form
provide clojure function as constructor
accept named parameters (maps) in constructor
participate in pre/post walk multi-method