【问题标题】:Macros iterating over undefined Symbols迭代未定义符号的宏
【发布时间】:2011-10-21 17:52:53
【问题描述】:

当使用另一个宏多次应用一个宏时,裸符号不会插入到当前上下文中:

(defmacro ty [type]
  `(deftype ~type []))

(defmacro empties [& args]
  (doseq [arg args]
    `(ty ~arg))
  )

(empties Base Person Animal)
;equivalent to:
;(ty Base)
;(ty Person)
;(ty Animal)


(derive ::Person ::Base)
(derive ::Animal ::Base)
(ty Me)
(prn ::Me)
(prn Me)
(empties Empty)
(prn ::Empty)
(prn Empty)

最后一行给出:“无法解析符号:在此上下文中为空”,即使在使用直接宏 ty 时,它也可以工作。有什么办法可以解决这个问题?如果可能没有 eval 会更好。

【问题讨论】:

    标签: macros clojure metaprogramming


    【解决方案1】:
    (defmacro empties [& args]
      (doseq [arg args]
        `(ty ~arg)))
    
    (empties Base Person Animal)
    ;equivalent to:
    ;(ty Base)
    ;(ty Person)
    ;(ty Animal)
    

    这是错误的。您的empties 调用意味着empties 的宏扩展函数将符号BasePersonAnimal 作为参数。然后它为每个计算 ty 宏调用,但不返回任何内容,因为 doseq 总是返回 nil。因此,来自 empties 调用的扩展代码为零。您需要从宏函数返回一个表单。您应该将多个表单包装到 do 中,并实际将所有子表单返回给它:

    (defmacro empties [& args]
      `(do ~@(map (fn [arg]
                   `(ty ~arg))
                  args)))
    

    【讨论】:

    • 有趣。请注意,您的解决方案似乎对我不起作用:I get IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol on the "do" line
    • (fn [arg]),而不是(fn (arg))。但总的来说他是对的:doseq 在宏中通常是错误的,当然如果这是目标,也可以将doseq 扩展为
    【解决方案2】:

    FWIW,我更喜欢将@Svante 的解决方案写成

    (defmacro empties [& args]
      (cons `do
            (for [arg args]
              `(ty ~arg))))
    

    这也非常接近您的 doseq 方法的结构。

    【讨论】:

    • @daniel-ribeiro 为我工作。 user=> (empties Empty) | user.Empty | user=> (prn Empty) | user.Empty.
    • 谢谢@kotarak。我在 REPL 上使用过,效果很好。 Intellij 的 LaClojure 搞砸了一些东西,它不起作用。诡异的。对于之前没有尝试过 REPL,我深表歉意。
    • 注意:这个答案比 Svante 的答案正确且简单。然而,他的回答解释了为什么这个是正确的。希望能够同时选择两者。
    猜你喜欢
    • 2022-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多