为什么会失败:
(eval (with-meta '(fn [] 0) {:stack (gensym "overflow")}))
; Syntax error compiling at (REPL:1:1).
; Unable to resolve symbol: overflow210 in this context
当以下都没有失败时?
(eval (with-meta '(do [] 0) {:stack (gensym "overflow")}))
; 0
(eval (with-meta '(let [] 0) {:stack (gensym "overflow")}))
; 0
(eval (with-meta '(if true 0 1) {:stack (gensym "overflow")}))
; 0
(eval (with-meta '(println "hello") {:stack (gensym "overflow")}))
; hello
; nil
上面的例子是我试图找到一个最小的、可重现的例子。我在处理宏时遇到了这个问题,这里有一个简化的示例:
(defmacro my-macro []
(with-meta '(fn [] 0) {:stack (gensym "overflow")}))
(my-macro)
; Syntax error compiling at (REPL:1:1).
; Unable to resolve symbol: overflow156 in this context
尝试遵循 testing Clojure macros 上这篇文章中解释的模型.
最佳答案
问得好!深入研究这一点很有趣。
首先尝试将元数据映射设置为有效的内容,然后在每个示例中检索它,这会很有帮助:
(meta (eval (with-meta '(fn [] 0) {:ten 10})))
;;=> {:ten 10}
(meta (eval (with-meta '(do [] 0) {:ten 10})))
;;=> nil
(meta (eval (with-meta '(let [] 0) {:ten 10})))
;;=> nil
(meta (eval (with-meta '(if true 0 1) {:ten 10})))
;;=> nil
(meta (eval (with-meta '(println "hello") {:ten 10})))
;; printed: hello
;;=> nil
希望这能让您了解这里发生的情况:元数据不会作为非 fn
表单值的一部分返回,因此不会对其进行评估。我们可以用另一个使用元数据的值(例如向量)来测试这个假设:
(meta (eval (with-meta '[1] {:ten 10})))
;;=> {:ten 10}
但是使用gensym
:
(eval (with-meta '[1] {:stack (gensym "overflow")}))
;;=> Syntax error compiling at (tmp:localhost:35479(clj)*:25:7).
;;=> Unable to resolve symbol: overflow6261 in this context
您可以看到where this is emitted in the Clojure compiler ,并且搜索 new MetaExpr
将向您显示发出元数据计算的其他位置(我可以看到向量、映射、集合、函数和 reify
)。
tl,dr: Clojure 评估函数形式的元数据,因为它将评估的元数据附加到结果函数。 Clojure 语法中支持的数据文字也是如此。表单上的任何其他元数据都会被编译删除,因此不会对其进行评估,因此不会导致符号解析错误。
关于clojure - 为什么在 Clojure 中向函数定义添加元数据的工作方式与其他形式不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65515096/