clojure - 为什么在 Clojure 中向函数定义添加元数据的工作方式与其他形式不同?

标签 clojure

为什么会失败:

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

相关文章:

algorithm - 懒惰地生成排列

macros - Clojure 宏定义令人困惑

r - 阶数 (n) 的修正贝塞尔函数

clojure - Clojure 中类型和类的区别

clojure - 如何在 ClojureScript 的 cljs.core 命名空间中扩展宏

recursion - 不在尾部位置重复

javascript - Clojure[Script] 中映射的 ...rest 相当于什么?

clojure - 无法在此服务器请求中解析出 EDN

clojure - 输入验证是 Clojure 中多方法的合理用例吗?

recursion - 在 Clojure 中,我可以在哪里将 print 函数放置在循环/递归中?