11

以下两个在宏中使用函数的示例会导致评估没有错误。

(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

可能是什么原因?fnmy-nullary-fn和所返回的函数对象之间constantly有区别my-unary-fn吗?

我正在运行 Clojure 1.5.1。

CLJ-946可能与此有关。

4

2 回答 2

7

看看clojure.lang.Compiler.ObjExpr#emitValue()。直接出现在代码中的任何实例对象(或生成的代码,在宏扩展结果的情况下)必须:

  • 属于类型编译器知道如何实例化或发出引用;或者
  • 已经print-dup定义了它们的类型,在这种情况下,编译器通过阅读器的往返发出对象实例化。

函数对象确实有一个print-dup实现,但它构造了只调用函数类构造函数的 0 参数版本的读取评估形式:

(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 ...

所以现在你知道了,并且知道为什么要避免将函数对象直接插入到生成的代码中,即使它“有效”。

于 2013-10-18T14:46:48.873 回答
1

请参阅这个也引发异常的示例:

(defmacro does-also-not-work []
  (let [x 4
        f (fn [] x)]
    `(~f)))

就像 的结果一样constantly,但与前两个示例不同,f这里是一个闭包。显然,在宏扩展期间创建的闭包不会持续存在。

于 2013-10-17T14:56:35.637 回答