Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure: Unable to find static field

Tags:

clojure

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
like image 581
heyarne Avatar asked Feb 04 '16 11:02

heyarne


3 Answers

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).

like image 190
leetwinski Avatar answered Nov 20 '22 06:11

leetwinski


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.

like image 6
ntalbs Avatar answered Nov 20 '22 08:11

ntalbs


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.

like image 3
johnbakers Avatar answered Nov 20 '22 08:11

johnbakers