在Clojure中,为什么向函数定义添加元数据的工作方式不同于其他形式?
为什么会失败:在Clojure中,为什么向函数定义添加元数据的工作方式不同于其他形式?,clojure,Clojure,为什么会失败: (eval(带meta'(fn[]0){:stack(gensym“overflow”)}) ; 在(REPL:1:1)处编译时出现语法错误。 ; 无法在此上下文中解析符号:overflow210 当下列各项都没有失败时 (eval(带meta'(do[]0){:stack(gensym“overflow”)}) ; 0 (eval(带meta'(let[]0{:stack(gensym“overflow”)})) ; 0 (eval(带meta'(如果为真01){:stack
(eval(带meta'(fn[]0){:stack(gensym“overflow”)})
; 在(REPL:1:1)处编译时出现语法错误。
; 无法在此上下文中解析符号:overflow210
当下列各项都没有失败时
(eval(带meta'(do[]0){:stack(gensym“overflow”)})
; 0
(eval(带meta'(let[]0{:stack(gensym“overflow”)}))
; 0
(eval(带meta'(如果为真01){:stack(gensym“overflow”)})
; 0
(eval(带meta)(println“hello”){:stack(gensym“overflow”)})
; 你好
; 无
上面的例子是我试图找到一个最小的,可重复的例子。我在处理宏时遇到了这个问题,这里有一个简化的示例:
(defmacro my macro[]
(带meta'(fn[]0){:stack(gensym“overflow”)})
(我的宏)
; 在(REPL:1:1)处编译时出现语法错误。
; 无法在此上下文中解析符号:overflow156
在尝试遵循上的这篇文章中解释的模型时,问题在于您使用的是
gensym
,而不是元数据。注意:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(dotest
(binding [*print-meta* true]
(let [
fn-code-plain '(fn [] 42)
fn-code-meta (with-meta '(fn [] 42) {:alpha true})
fn-code-sym-meta (with-meta '(fn [] 42) {:alpha (gensym "dummy")})
]
(prn fn-code-plain)
(prn fn-code-meta)
\
(prn (eval fn-code-plain))
(prn (eval fn-code-meta))
(newline)
(prn fn-code-sym-meta)
(throws? (eval fn-code-sym-meta))
)
))
结果:
^{:line 7, :column 27} (fn [] 42)
^{:alpha true} (fn [] 42)
#object[user$eval19866$fn__19867 0xac891b5 "user$eval19866$fn__19867@ac891b5"]
^{:alpha true} #object[user$eval19870$fn__19871 0x2d1ab7e0 "user$eval19870$fn__19871@2d1ab7e0"]
^{:alpha dummy19865} (fn [] 42)
^{:alpha "Forty-Two!"} (fn [] 42)
^{:alpha "Forty-Two!"} #object[user$eval20082$fn__20083 0x24a63de5 "user$eval20082$fn__20083@24a63de5"]
问题是eval
看到符号dummy19865
并试图解决它。它是由gensym创建的这一事实与此无关。关键字没有问题:
fn-code-kw-meta (with-meta '(fn [] 42) {:alpha :dummy-42})
<snip>
(prn fn-code-kw-meta)
(prn (eval fn-code-kw-meta))
或定义的符号:
(def mysym "Forty-Two!")
<snip>
fn-code-mysym-meta (with-meta '(fn [] 42) {:alpha mysym})
<snip>
(prn fn-code-mysym-meta)
(prn (eval fn-code-mysym-meta))
总结: 您已经演示了
eval
仅尝试对fn
表单的元数据进行符号解析,而不尝试其他特殊表单,如do
、let
、if
,或使用预先存在的函数,如println
。如果您希望进一步探索,您可能应该查询Clojure电子邮件列表:
clojure@googlegroups.com
以上代码基于。好问题!这很有趣 首先尝试将元数据映射设置为有效的,并在每个示例中检索它,这很有帮助:
(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
您可以看到,搜索新的MetaExpr
将向您显示元数据评估发出的其他位置(我可以看到向量、映射、集合、函数和具体化
)
tl,dr:Clojure评估函数表单的元数据,因为它将评估的元数据附加到结果函数。Clojure语法中支持的数据文本也是如此。表单上的任何其他元数据都会被编译剥离,因此不会对其进行计算,因此不会导致符号解析错误。我认为在简化示例的过程中,丢失了太多的上下文。我最初在尝试向宏的输出添加元数据时遇到了这个问题(eval没有直接参与)。更多信息,请参阅我的更新帖子!有关完整信息,请参阅更新的答案。另外,为什么在元数据中使用
gensym
?你能用一个简单的字符串吗?谢谢你更新的答案。你最后的总结很好地阐述了当前的情况。我最初是在读了这篇文章之后遇到这个问题的:,发现返回fn
表单会出现问题,而其他表单则不会。关于测试宏的有趣文章。我通常只测试宏的功能,而不编写单元测试来验证扩展。请看这个答案,我是如何一步一步建立一个宏观的:对这个主题的极好探索!谢谢你走过如何得到答案的过程,我从中学到了很多!
(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