Behaviour of special variables under macro expansion
common-lisp, macros, special-variables
Solution
When the REPL is told to evaluate `(bot)`, it first has to perform macroexpansion. It calls the macroexpansion function `bot`, which means, in effect, evaluating
(let ((*foo* 17))
`(bar))
That returns `(bar)` and then the binding of from `let` is unwound. Now we've got `(bar)`. `bar` is a macro, so it's time for another round of macroexpansion, which means evaluating
(progn
(format t "foo: ~a" *foo*)
`(+ 1 1))
which 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:
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
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:
(defmacro baz (&environment env)
(let ((*foo* 42))
(macroexpand '(bar) env)))
Problem
``` 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?