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
的宏扩展,打印当前值{{1} }(正在*foo*
),并返回格式17
,这不是宏,宏扩展时间现在结束,最后评估表单(+ 1 1)
,然后返回(+ 1 1)
为什么我错了?
有没有一种简单的方法可以做我想要的事情?
答案 0 :(得分:5)
当REPL被告知评估(bot)
时,它首先必须执行宏展开。它调用宏展开函数bot
,这实际上意味着评估
(let ((*foo* 17))
`(bar))
返回(bar)
,然后解开let
的绑定。现在我们已经(bar)
了。 bar
是一个宏,所以是时候进行另一轮宏观扩展,这意味着评估
(progn
(format t "foo: ~a" *foo*)
`(+ 1 1))
打印foo: NIL
,然后返回(+ 1 1)
。
如果希望在某些绑定的范围内执行宏展开,则需要自己调用宏展开函数。例如,您可以使用macroexpand:
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
但是,如果您要自己进行宏扩展,请务必保留environment arguments。在这种情况下,baz
的更好定义是:
(defmacro baz (&environment env)
(let ((*foo* 42))
(macroexpand '(bar) env)))