When working with loop
, it sounds like the Clojure compiler tries to use primitive variables for better performance. From the "Support for Java Primitives" section.
let/loop-bound locals can be of primitive types, having the inferred, possibly primitive type of their init-form.
recur forms that rebind primitive locals do so without boxing, and do type-checking for same primitive type.
The below loop throws a syntax error.
(loop [x (pos? 1)]
(when-not x
(recur (first [false]))))
Syntax error (IllegalArgumentException) compiling fn* at (src/foo.clj:4:1).
recur arg for primitive local: x is not matching primitive, had: Object, needed: boolean
The compiler appears to know that pos?
returns a primitive boolean
and (first [false])
does not (returns a boxed boolean Boolean
?). Wrapping the recur arg in a call to boolean
compiles.
(loop [x (pos? 1)]
(when-not x
(recur (boolean (first [false])))))
=> nil
What's interesting, however, is changing the recur
arg to different functions.
(loop [x (pos? 1)]
(when-not x
(recur (pos? x))))
Syntax error (IllegalArgumentException) compiling fn* at (src/foo.clj:4:1).
recur arg for primitive local: x is not matching primitive, had: Object, needed: boolean
Given the compiler knows pos?
returns a primitive in the init form, it seems it should know it'd also return a primitive in the recur arg.
Like before, wrapping the pos?
recur call in boolean
allows the form to compile.
(loop [x (pos? 1)]
(when-not x
(recur (boolean (pos? x)))))
=> nil
If I change the arg to boolean
to be a literal, I get the syntax error.
(loop [x (pos? 1)]
(when-not x
(recur (boolean 1))))
Syntax error (IllegalArgumentException) compiling fn* at (src/foo.clj:4:1).
recur arg for primitive local: x is not matching primitive, had: Object, needed: boolean
That only occurs for numbers though. Passing a string, char, vector, or even a boolean all compile. e.g.,
(loop [x (pos? 1)]
(when-not x
(recur (boolean true))))
=> nil
Passing a literal boolean does not work.
(loop [x (pos? 1)]
(when-not x
(recur false)))
Syntax error (IllegalArgumentException) compiling fn* at (src/foo.clj:4:1).
recur arg for primitive local: x is not matching primitive, had: java.lang.Boolean, needed: boolean
pos?
is an inlined function. In the above cases it expands to (. clojure.lang.Numbers (isPos 1))
. From the source of isPos
, we find a static method that has the return type boolean
.
static public boolean isPos(Object x){
return ops(x).isPos((Number)x);
}
Since isPos
is a static method, the compiler should always know it's type. From Type Hints:
Note that type hints are not needed for static members (or their return values!) as the compiler always has the type for statics.
Except the first example and the ones with literals, all the above functions call to static Java methods that return a primitive boolean. Why does the compiler not see the primitive type return value in the above cases?