经常在语法引用中不起作用

时间:2014-05-09 20:33:55

标签: clojure

如果我这样做:

(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 

由于两者都是等价的,为什么代码一直不起作用?

1 个答案:

答案 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的函数不能像这样评估。