【发布时间】:2013-09-16 02:07:24
【问题描述】:
如果我评估
(def ^:macro my-defn1 #'defn)
定义了一个名为“my-defn1”的宏,我可以像使用“defn”一样使用它。
但是,如果我改为评估
(if true
(def ^:macro my-defn2 #'defn))
'my-defn2' 的 var 没有 :macro 元数据集,我不能将它用作宏,即使 'def' 形式与前一种情况相同。
这是完整的代码(http://cljbin.com/paste/52322ba5e4b0fa645e7f9243):
(def ^:macro my-defn1 #'defn)
(if true
(def ^:macro my-defn2 #'defn))
(println (meta #'my-defn1)) ; => contains :macro
(println (meta #'my-defn2)) ; => doesn't contain :macro!
(my-defn1 hello1 []
(println "hello 1"))
(hello1) ; => prints "hello 1"
(my-defn2 hello2 [] ; => CompilerException: Unable to resolve
(println "hello 2")) ; symbol: hello2 in this context
是什么让行为与众不同?
【问题讨论】:
-
我浏览了 Clojure 源代码一个小时。这似乎真的很难。我想这与改变元数据中包含 :macro 的 var 的根删除 :macro 条目这一事实有关。这与解析 def 表单时创建未绑定 var 的事实无关,因为它是在没有元数据的情况下创建的。我希望我有更多的时间来解决这个问题。在条件表达式中使用 def 肯定不是一个好习惯,但我赞成你的问题,因为我们可以从令人满意的答案中了解 JVM Clojure 实现的机制
-
您认为 Joost 的回答符合您的假设吗?也许条件 def 被拆分为一个声明,然后是一个 alter-var-root,它忽略了 :macro 声明?感谢回复!
-
不,我不认为他回答了你的问题,但回避了 - 抱歉,Joost。 if 和 def 都是特殊形式,这意味着它们的参数是独立于评估进行解析的。您可以在 Clojure 编译器 (Compiler.java) 的源代码中找到它们的实现。如果元数据实现了 IMeta,则读取器将元数据关联到下一个对象/评估表单。 Def 稍后将这些数据与 var 相关联。它需要对源代码进行彻底的研究和仔细研究,以想象如果在 if 中使用 def 会发生什么。当然,def 不打算以这种方式使用,并且
-
这就是为什么您发现的错误可能已为开发人员所知,但为了其他好处而忽略了。
-
感谢您的回复。关于这个问题,我在 Clojure 的 Jira 上创建了一个 issue。我们会看看情况如何。