Clojure中具有闭包和eval的函数

时间:2012-06-25 15:10:34

标签: clojure closures eval

eval一起使用时,带闭包的函数似乎会中断。

(eval {:fn (let [x "foo"] (fn [] "x"))})
;=> {:fn #<user$eval14716$fn__14717 user$eval14716$fn__14717@1ddd735>}

(eval {:fn (let [x "foo"] (fn [] x))})
;=> IllegalArgumentException No matching ctor found for class user$eval14740$fn__14741 
;   clojure.lang.Reflector.invokeConstructor (Reflector.java:166)

我对Clojure(或封闭)知之甚少,不知道这是一个错误还是故意不被允许的东西 - 有人可以对此有所了解吗?

编辑:为了清楚起见,我正在谈论eval处理函数对象的方式。 AFAIK eval实际上设计用于处理java对象,包括函数;在clojure网站上给出的示例 - (eval (list + 1 2 3)) - 将函数对象传递给eval

3 个答案:

答案 0 :(得分:2)

Cloure的eval并不完全支持函数对象。它不一定是导致问题的闭包。

例如,这在Clojure 1.0.0

中无效
(eval {:fn (fn [x] x)})

但是这样做了:

(eval (fn [x] x))

第一个例子得到修复。以下也有效:

(eval (let [x "foo"] (fn [] x)))

但以下情况仍无效:

(eval {:fn (let [x "foo"] (fn [] x))})

我无法将其归结为编译器中的单行,但它是关于eval在不同上下文中如何处理文字对象(clojure.lang.Compiler $ ObjExpr)的原因:例如:在表达式的“顶部”与另一个数据结构内部。

一般来说,我认为,你不能依赖于能够在Clojure中使用eval函数对象,无论它们是否是闭包。它恰好适用于一些简单的例子,主要是为了简化(eval (list + 1 2))之类的解释。宏应始终将文字源代码作为数据结构返回,而不是编译函数。

答案 1 :(得分:1)

尝试引用你的参数eval:

(eval '{:fn (let [x "foo"] (fn [] x))})
;=> {:fn #<user$eval345$fn__346 user$eval345$fn__346@17b6dd83>}
((:fn *1))
;=> "foo"

答案 2 :(得分:1)

这不是错误。具有“结束”的(eval (list + 1 2 3))相当于(eval (list fn [] "foo"))不是 (eval (fn [] "foo"))

而且(eval (list fn [] "foo")) => Can't take value of a macro: #'clojure.core/fn,再次表明你不应该做那样的事情(而且无论如何也没有必要)。