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
My mental model (clearly wrong) of macro expansion says the following happens in order:
Run the macro expansion of bot
(which binds *foo*
to 17
), run the macro expansion of bar
, which prints the current value of *foo*
(being 17
), and returns the form (+ 1 1)
, which is not a macro, macro expansion time is now over, finally evaluate the form (+ 1 1)
, and returns 2
.
Why am I wrong?
Is there an easy way to do what I intend?
When the REPL is told to evaluate
(bot)
, it first has to perform macroexpansion. It calls the macroexpansion functionbot
, which means, in effect, evaluatingThat returns
(bar)
and then the binding of fromlet
is unwound. Now we've got(bar)
.bar
is a macro, so it's time for another round of macroexpansion, which means evaluatingwhich prints
foo: NIL
, and returns(+ 1 1)
.If you want the macroexpansion to be performed in the scope of some bindings, you'll need to call the macroexpansion function yourself. E.g., you can use macroexpand:
But, if you're going to do macroexpansion yourself, be sure to preserve environment arguments. In this case, a better definition of
baz
would be: