如果我这样做:
(eval (let [f (fn [& _] 10)]
`(~f nil)))
按预期返回10.
虽然如果我这样做:
(eval (let [f (constantly 10)]
`(~f nil)))
它抛出异常:
IllegalArgumentException No matching ctor found for
class clojure.core$constantly$fn__... clojure.lang.Reflector.invokeConstructor
由于两者都是等价的,为什么代码一直不起作用?
答案 0 :(得分:4)
这个问题有两个答案,真正得到它。
首先,为了澄清您的eval
表单在第二种情况下没有给出预期结果的原因,请注意f
已被指定为等于功能 (fn [& _] 10)
。这意味着在评估该表单时,将再次评估函数对象 - 可能不是您的想法。
tl; dr:f
在绑定时进行评估,当您创建的表单为eval
时,会再次(定义不正确)
一个(匿名函数)工作的原因,而另一个失败意味着我们必须查看评估过程的一些内部。
当Clojure评估对象表达式(如函数对象形成的表达式)时,它在clojure.lang.Compiler$ObjExpr
public Object eval() {
if(isDeftype())
return null;
try
{
return getCompiledClass().newInstance();
}
catch(Exception e)
{
throw Util.sneakyThrow(e);
}
}
在REPL上试试这个:
以匿名函数开头:
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
请注意,newInstance
上的Class
会调用该类&#39; nullary构造函数 - 一个不带参数的构造函数。
现在尝试关闭某些值的函数
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 [...]
由于闭包的upvalues是在构造时设置的,所以这种函数类没有nullary构造函数,并且创建一个没有上下文的新函数类失败。
最后,请查看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))
constantly
返回的函数关闭x
,因此编译器无法eval
这种功能。
tl; dr(再次):可以用这种方式评估没有upvalues的函数,并生成相同函数的新实例。具有upvalues的函数不能像这样评估。