Clojure宏:解析变量

时间:2016-06-27 07:43:35

标签: clojure macros

我似乎可以在Clojure中围绕宏。我可能遗漏了一些基本的东西。首先,请允许我描述一下我想要的例子。

(defmacro macrotest [v] `(d ...))
(def a '(b c))
(macroexpand-1 '(macrotest a))
; => (d (b c))

换句话说,传递给var的{​​{1}}已解决,但未进一步评估。

为宏提供var的值:

macrotest

但是提供var不会:

(defmacro macrotest [v] `(d ~v))
(macroexpand-1 '(macrotest (b c)))
; => (d (b c))

是否可以在Clojure宏中解析 (def a '(b c)) (macroexpand-1 '(macrotest a)) ; => (d a) ,但不能评估其值?

编辑:var

似乎可以实现我的目标
eval

2 个答案:

答案 0 :(得分:2)

了解宏在编译时评估非常重要,而不是在运行时。

在编译时var a还没有值,因此它的值不会传递给宏,只传递给它的名称。但是,当您显式传递“值”时 - 那么它在编译时存在,并且您看到它“有效”。

答案 1 :(得分:2)

词汇环境

在宏中使用eval是不好的做法。当然,以下工作:

(macroexpand-1 '(macrotest a))
; => (user/d (b c))

...但是,这不能按预期工作:

(macroexpand-1 '(let [a "oh no"]
                  (macrotest a)))
; => (user/d (b c))

当然,您希望宏评估a,因为它是在本地定义的,而不是使用绑定到它的全局变量,但是您不能,因为宏不会在正确的词汇上下文中评估v 。这是理解宏的关键:它们采用代码并生成代码;您尚未使用该代码操纵的任何数据。换句话说,扩展宏的时刻可能与它们生成的代码的计算时间完全无关:在执行宏时评估的任何内容都可能过早评估w.r.t.您的数据的相关性。

你想做什么?

有一个名为macrotest的表单应接受参数v,然后执行 - 如果您已应用表单d。但是:

  • (macrotest (b c)) 不应评估(b c),只需将其复制即可生成(d (b c))
  • (macrotest a) 评估a,这会生成引用的表单,并将该表单放在生成的代码中,同时返回(d (b c))

这里有一个问题,因为在一种情况下,含义会发生根本变化。要么你评估一个参数,在这种情况下你必须引用(b c)并写下:

(defmacro macrotest [v] `(d ~v))

...要求d准备处理引用的参数;或者你对没有评估的论点没问题。 使用与上面相同的d,您可以明确引用该参数:

(defmacro macrotest [v] `(d '~v))

但是,如果d本身是一个不评估其参数的宏,则必须避免在~v前面引用该引号。