为什么一个函数从宏中运行而另一个函数无法编译?

Ole*_*Cat 3 macros clojure

这是片段:

(defmacro produce-constantly-fn []
  (constantly :value))

(defmacro produce-fn []
  (fn [& args] :value))

(defn test-fn []
  ((produce-fn)))

;; during evaluation of form below it throws:
;; java.lang.IllegalArgumentException
;; No matching ctor found for class clojure.core$constantly$fn__4614
(defn test-constantly-fn []
  ((produce-constantly-fn)))
Run Code Online (Sandbox Code Playgroud)

为什么上一个函数无法编译?该片段可以被视为某种排序宏滥用,但无论如何......

Pio*_*dyl 5

我假设您在没有引用的情况下定义了您的宏体,并且您很好奇为什么它会导致如此奇怪的错误消息.如果您真的打算定义一个用于调用的宏,(constantly :value)那么您应该使用引用,它将起作用:

(defmacro produce-constantly-fn []
  `(constantly :value))

(defn test-constantly-fn []
  ((produce-constantly-fn)))

=> #'user/test-constantly-fn

(test-constantly-fn)
=> :value
Run Code Online (Sandbox Code Playgroud)

现在回到你的案子而不引用.它看起来非常有趣和神秘,所以我做了一些挖掘.这些是我的发现:

定义宏时:

(defmacro produce-constantly-fn []
  (constantly :value))
Run Code Online (Sandbox Code Playgroud)

它只会创建一个名为的函数,produce-constantly-fn并将其标记为一个宏(它仍然是一个Clojure函数).

当你查看实现时constantly你会发现(docs和meta省略):

(defn constantly [x]
  (fn [& args] x))
Run Code Online (Sandbox Code Playgroud)

它将编译为一个闭包对象,它将实现IFn并将有一个构造函数参数来关闭x参数.Java代码中有类似的东西:

public class clojure.core$constantly$fn__4614 {
    private final Object x;
    public clojure.core$constantly$fn__4614(Object x) {
        this.x = x;
    }

    public Object invoke(...) {
        return x;
    }
    // other invoke arities
}
Run Code Online (Sandbox Code Playgroud)

现在,当你有跟随sexp:

(defn test-constantly-fn []
  ((produce-constantly-fn)))
Run Code Online (Sandbox Code Playgroud)

我注意到Clojure读者只能(produce-constantly-fn)返回一个函数对象(通过调用生成(constantly :value)),但我在调试器中发现它生成clojure.core$constantly$fn__4614.符号(注意符号.的末尾 - 它是一个用于调用构造函数的Java互操作形式).看起来函数对象/值以某种方式转换为表示其构造函数调用的符号.我可以发现函数值被转换为Compiler$InvokeExpr包含对已编译类名的引用的对象,这可能以某种方式转换为符号.

读者试图clojure.core$constantly$fn__4614.进一步解决.它被读者转换为对clojure.core$constantly$fn__4614clojure.core$constantly$fn__4614没有参数的类构造函数的调用.

正如您在上面看到的那样,该类的构造函数只需要一个构造函数,因此编译失败(在clojure.lang.Compiler.NewExpr构造函数体中):

java.lang.IllegalArgumentException: No matching ctor found for class clojure.core$constantly$fn__4614
Run Code Online (Sandbox Code Playgroud)

我不确定为什么Clojure读取器将函数值转换为带有构造函数调用互操作形式的符号并导致此类行为,因此我只提供了错误消息的直接原因,而不是代码无法工作的根本原因.我想这可能是一个错误或者是一个有意识的设计决定.从宏作者那里最好快速失败并了解宏的返回值不是有效的代码数据,但另一方面,确定返回的数据是否是有效代码可能非常困难或不可能.值得查看Clojure邮件列表.