如何在另一个宏的范围内扩展宏(尝试调试宏)

时间:2014-07-25 11:32:14

标签: macros clojure

这是我可以做的最简单的例子:

(defmacro printer [& forms]
  `(println ~@forms))

(defmacro adder [s]
  `(inc ~s))

可以按预期使用它们:

(printer "haha")
=> "haha"

(adder 1)
=> 2

我可以macroexpand他们看看宏做了什么:

(macroexpand '(printer 1))
=> (clojure.core/println 1)

(macroexpand '(adder 1))
=> (clojure.core/inc 1)

但是当它们嵌套时我得不到我想要的东西:

(macroexpand '(printer (adder 1)))
=> (clojure.core.println (adder 1))

我希望得到

=> (clojure.core.println (clojure.core/inc 1))

有没有办法扩展嵌套宏? 这对我调试特定错误有很大帮助。

2 个答案:

答案 0 :(得分:5)

您在macroexpand-all

之后
(use 'clojure.walk)
(macroexpand-all '(printer (adder 1)))
;(clojure.core/println (clojure.core/inc 1))

答案 1 :(得分:1)

感谢接受我的回答,我很高兴它有所帮助。我忘了补充一点,如果你想完全扩展编译器看到的内容,你可以利用clojure分析器的强大功能:

(use '[clojure.tools.analyzer.passes.jvm.emit-form :only [emit-form emit-hygienic-form]]
     '[clojure.tools.analyzer.jvm :only [analyze]])

(emit-form (analyze '(printer (adder 1))))

(emit-hygienic-form (analyze '(printer (adder 1))))

上述两个结果与macroexpand-all的结果几乎相同,但应涵盖macroexpand-all可能无法完全发挥作用的极端情况。在您的示例中,它将inc扩展为inc定义内的内联函数。此外,如果要检查阴影,emit-hygienic-form非常有用:

(emit-form (analyze '(let [a 1 a a] a)))
;=> (let* [a 1 a a] a)

(emit-hygienic-form (analyze '(let [a 1 a a] a)))
;=> (let* [a__#0 1 a__#1 a__#0] a__#1)

在一天结束时,macroexpand-1 macroexpand-all emit-formemit-hygienic-form对于调试clojure宏非常有用。