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)))