Consider the following sequence of forms in Common Lisp (evaluations performed in SBCL 2.4.2 for Windows):
(defmacro double-g (x)
(list (quote +) x x))
(macroexpand-1 (quote (double-g 3))) => (+ 3 3), T
(macrolet ((double-l (x) (list (quote +) x x)))
(macroexpand-1 (quote (double-l 3))))
=> (DOUBLE-L 3), NIL
Can someone help me understand why the second and third forms evaluate to different results?
I expected the second and third forms to evaluate to the same result.
Problem: you are trying to expand the macro in the wrong environment. The macrolet
defines a local macro, but macroexpand-1
does not see it, because it does not get the lexical environment passed.
We'll modify the macro expander:
CL-USER 23 > (defmacro expand-1-form (form &environment env)
(list 'quote (macroexpand-1 form env)))
EXPAND-1-FORM
Above defines a macro expand-1-form
, which gets passed the current environment, which then is available via the variable env
. The macro expands this form by using macroexpand-1
and also passes the environment to it. The macro expand-1-form
gets the environment automagically passed into the environment variable env
(which we defined in the parameter list). The expansion is returned as a quoted form.
Now we can expand the local macro double-l
using the macro expand-1-form
:
CL-USER 24 > (macrolet ((double-l (x) (list (quote +) x x)))
(expand-1-form (double-l 3)))
(+ 3 3)
Above expands the macro expand-1-form
, passing the current environment. This then calls macroexpand-1
with that environment.
We can also write it as nested macrolet
s:
CL-USER 25 > (macrolet ((expand-1-form (form &environment env)
(list 'quote (macroexpand-1 form env))))
(macrolet ((double-l (x)
(list (quote +) x x)))
(expand-1-form (double-l 3))))
(+ 3 3)