27

Given the following piece of code:

(map Integer/parseInt ["1" "2" "3" "4"])

Why do I get the following exception unless I wrap Integer/parseInt in an anonymous function and call it manually (#(Integer/parseInt %))?

clojure.lang.Compiler$CompilerException: java.lang.RuntimeException: Unable to find static field: parseInt in class java.lang.Integer
1
  • 3
    in addition to the excellent answers you have already - Clojure is a fairly thin abstraction over the jvm, and there is no such thing as a first class method, so a method can't be an argument to a Clojure function. That is, on a byte code level, a method can't be an argument. Commented Feb 4, 2016 at 13:13

3 Answers 3

39

the documentation on java interop says the following:

The preferred idiomatic forms for accessing field or method members are given above. The instance member form works for both fields and methods. The instanceField form is preferred for fields and required if both a field and a 0-argument method of the same name exist. They all expand into calls to the dot operator (described below) at macroexpansion time. The expansions are as follows: ... (Classname/staticMethod args*) ==> (. Classname staticMethod args*) Classname/staticField ==> (. Classname staticField)

so you should remember, that Class/fieldName is just a sugar for getting a static field, neither static method call, nor reference to the static method (java method is not a clojure function really), so there is no static field parseInt in Integer class, while (Class/fieldName arg) calls a static method, they are two totally different operations, using the similar sugary syntax.

so when you do (map #(Integer/parseInt %) ["1" "2" "3" "4"]) it expands to

(map #(. Integer parseInt %) ["1" "2" "3" "4"])

(you can easily see it yourself with macroexpansion),

and (map Integer/parseInt ["1" "2" "3"]) expands to

(map (. Integer parseInt) ["1" "2" "3"])

It fails when it is trying to get a field (which you think is getting a reference to a method).

Sign up to request clarification or add additional context in comments.

Comments

6

Integer/parseInt is a static method of Integer class, not a clojure function. Each clojure function is compiled to java class which implements clojure.lang.IFn interface. map expects clojure function (which implements IFn interface) as a first argument, however, Integer/parseInt is not.

You can check that in the clojure repl.

user=> (type map)
clojure.core$map
user=> (type Integer)
java.lang.Class
user=> (type Integer/parseInt)

CompilerException java.lang.RuntimeException: Unable to find static field: parseInt in class java.lang.Integer, compiling:(/private/var/folders/p_/psdvlp_12sdcxq07pp07p_ycs_v5qf/T/form-init4110003279275246905.clj:1:1)

user=> (defn f [] 1)
#'user/f
user=> (type f)
user$f
user=> (type #(1))
user$eval9947$fn__9948

Perhaps reading this stackoverflow question will help you understand what is going on.

2 Comments

Thinking in terms of Clojure (rather than Java implementation), you would say that the function argument to the map function is the first argument. Bit confusing to hear it called the second argument.
@ChrisMurphy You're correct. It was a mistake. I updated the post.
3

You might prefer to do it without the Java interop:

(map read-string ["1" "2"])

or for a more safe variant:

(map clojure.edn/read-string ["1" "2"])

I personally prefer to minimize the use of Java as much as possible in Clojure code.

As for why you can't just pass the Java function, because map expects a function in Clojure, not a function in Java.

1 Comment

read-string is almost always a bad idea. documentation says: "Note that read-string can execute code (controlled by read-eval), and as such should be used only with trusted sources. For data structure interop use clojure.edn/read-string". try this one in your repl (read-string "#=(spit \"./filename.sh\" \"evil code\")") . As you almost always use data conversion for external data, read-string is highly unsafe here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.