宏观扩展下特殊变量的行为


FUZZ> (defvar *foo* nil)
*FOO*
FUZZ> (defmacro bar ()
        (format t "foo: ~A" *foo*)
        `(+ 1 1))
BAR
FUZZ> (defmacro bot ()
        (let ((*foo* 17))
          `(bar)))
BOT
FUZZ> (bot)
foo: NIL

我的宏观扩张心理模型(显然是错误的)说,以下情况按顺序发生:

运行 bot 的宏展开(将*foo*绑定到 17),运行 bar 的宏展开,打印出 *foo* 的当前值(正在17),并返回表单(+ 1 1),这不是宏,宏展开时间到此结束,最后计算表单(+ 1 1),并返回2

为什么我错了?

有没有一种简单的方法来做我想要的?

当 REPL 被告知要评估(bot)时,它首先必须执行宏展开。 它调用宏膨胀函数bot,这意味着,实际上,评估

(let ((*foo* 17))
  `(bar))

这将返回(bar),然后解除 from let 的绑定。 现在我们有了(bar). bar是一个宏观,所以是时候进行另一轮宏观扩张了,这意味着评估

(progn 
  (format t "foo: ~a" *foo*)
  `(+ 1 1))

打印foo: NIL,并返回(+ 1 1)

如果您希望在某些绑定的范围内执行宏扩展,则需要自己调用宏扩展函数。 例如,您可以使用宏展开

CL-USER> (defparameter *foo* nil)
*FOO*
CL-USER> (defmacro bar ()
           (format t "foo: ~a" *foo*)
           `(+ 1 1))
BAR
CL-USER> (defmacro baz ()
           (let ((*foo* 42))
             (macroexpand '(bar))))
BAZ
CL-USER> (baz)
foo: 42
2

但是,如果你要自己做宏观扩张,一定要保留环境参数。 在这种情况下,baz的更好定义是:

(defmacro baz (&environment env)
  (let ((*foo* 42))
    (macroexpand '(bar) env)))

相关内容

  • 没有找到相关文章

最新更新