This question has two answers, to really get it.
First, to clarify why your eval
form is not giving you the expected result in the second case, note that f
has been assigned to be equal to the function (fn [& _] 10)
. This means when that form is evaluated, the function object is again evaluated--probably not what you had in mind.
tl;dr: f
is evaluted when it is bound, and again (with ill-defined results) when the form you create is eval
'd.
The reason why one (the anonymous function) works, while the other fails means we have to look at some of the internals of the evaluation process.
When Clojure evaluates an object expression (like the expression formed by a function object), it uses the following method, in clojure.lang.Compiler$ObjExpr
public Object eval() {
if(isDeftype())
return null;
try
{
return getCompiledClass().newInstance();
}
catch(Exception e)
{
throw Util.sneakyThrow(e);
}
}
Try this at the REPL:
Start with an anonymous function:
user=> (fn [& _] 10)
#<user$eval141$fn__142 user$eval141$fn__142@2b2a5dd1>
user=> (.getClass *1)
user$eval141$fn__142
user=> (.newInstance *1)
#<user$eval141$fn__142 user$eval141$fn__142@ee7a10e> ; different instance
user=> (*1)
10
Note that newInstance
on Class
calls that class' nullary constructor -- one that takes no arguments.
Now try a function that closes over some values
user=> (let [x 10] #(+ x 1))
#<user$eval151$fn__152 user$eval151$fn__152@3a565388>
user=> (.getClass *1)
user$eval151$fn__152
user=> (.newInstance *1)
InstantiationException user$eval151$fn__152 [...]
Since the upvalues of a closure are set at construction, this kind of function class has no nullary constructor, and making a new one with no context fails.
Finally, look at the source of constantly
user=> (source constantly)
(defn constantly
"Returns a function that takes any number of arguments and returns x."
{:added "1.0"
:static true}
[x] (fn [& args] x))
The function returned by constantly
closes over x
so the compiler won't be able to eval
this kind of function.
tl;dr (again): Functions with no upvalues can be evaluated in this way and produce a new instance of the same function. Functions with upvalues can't be evaluated like this.