Search code examples
macroscommon-lisp

unexpected interaction between macroexpand-1 and macrolet


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.


Solution

  • 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 macrolets:

    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)