以下两个在宏中使用函数的示例会导致无错误的评估。
(defmacro works []
(let [f (fn [] 1)]
`(~f)))
(works)
;; => 1
(defn my-nullary-fn []
(fn [] 2))
(defmacro also-works []
(let [f (my-nullary-fn)]
`(~f)))
(also-works)
;; => 2
然而,
(defmacro does-not-work []
(let [f (constantly 3)]
`(~f)))
(does-not-work)
引发
java.lang.IllegalArgumentException: No matching ctor found
for class clojure.core$constantly$fn__4051
同样地,
(defn my-unary-fn [x]
(fn [] x))
(defmacro also-does-not-work []
(let [f (my-unary-fn 4)]
`(~f)))
(also-does-not-work)
引发
java.lang.IllegalArgumentException No matching ctor found
for class user$my_other_fn$fn__12802
可能是什么原因? fn
,my-nullary-fn
,constantly
和my-unary-fn
返回的函数对象之间是否存在差异?
我正在运行Clojure 1.5.1。
CLJ-946可能是相关的。
答案 0 :(得分:7)
看看clojure.lang.Compiler.ObjExpr#emitValue()
。直接出现在代码中的任何实例对象(或者在宏扩展结果的情况下生成的代码)都必须:
print-dup
,在这种情况下,编译器通过阅读器往返发出对象实例化。函数对象确实有print-dup
实现,但它构造了只读函数类构造函数的0参数版本的read-eval表单:
(print-dup (fn [] 1) *out*)
;; #=(user$eval24491$fn__24492. )
(let [x 1] (print-dup (fn [] x) *out*))
;; #=(user$eval24497$fn__24498. )
Clojure闭包是通过函数类实现的,函数类接受它们的闭合变量值作为构造函数参数。因此:
(let [f (fn [] 1)] (eval `(~f)))
;; 1
(let [x 1, f (fn [] x)] (eval `(~f)))
;; IllegalArgumentException No matching ctor found ...
现在你知道了,并且知道为什么要避免直接将函数对象插入到生成的代码中,即使它“有效”。
答案 1 :(得分:1)
请参阅此示例,它也会抛出异常:
(defmacro does-also-not-work []
(let [x 4
f (fn [] x)]
`(~f)))
就像constantly
的结果一样,但与前两个例子不同,f
这里是一个闭包。显然,在宏扩展期间创建的闭包不会持续存在。