【问题标题】:Clojure: def discards ^:macro when used outside the top-levelClojure: def 在顶层之外使用时丢弃 ^:macro
【发布时间】: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。我们会看看情况如何。

标签: macros clojure metadata


【解决方案1】:

Clojure 的 def 不能真正有条件地应用。 def 的文档在这方面不够强大。不仅风格不好,还会引发各种微妙的问题。

您应该只在顶层使用 def,或者在顶层使用 dolet 形式。有条件地应用 def 将导致功能被拆分为 declare 和随后的条件 def,但并不总是以您期望/喜欢的方式。

您最好在此处的顶层使用 def,然后有条件地使用 alter-var-root。或使用(def my-var (if .. .. ..))。想一想为什么你会想要一个全局定义“消失”。

【讨论】:

  • 您对我的问题对 Igrapenthin 的 cmets 有何评论?谢谢你的回答。
猜你喜欢
  • 2013-01-10
  • 1970-01-01
  • 2013-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-14
  • 1970-01-01
  • 2010-10-11
相关资源
最近更新 更多