[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Why does my Macro get expanded 8 times?



   Date: Tue, 12 Jul 88 11:24 EDT
   From: Charles Hornig <Hornig@ALDERAAN.SCRC.Symbolics.COM>

       Date: Mon, 11 Jul 88 22:33:39 PDT
       From: gyro@kestrel.ARPA (Scott B. Layson)

       To prevent multiple macro expansion, you could make your macro a
       so-called "displacing" macro, using ZL:DEFMACRO-DISPLACE.  This will
       cause it to modify the the cons which is the head of the form that
       invokes the macro (got that?), effectively caching the expansion
       there.  (Try it to see the details; it's really pretty
       straightforward.)

       SCRC folks: surely one of the ways you considered fixing the problem
       was to make all macros displace in Phase I.  Why doesn't this work?

   Because displacing macros are the wrong thing in a language with lexical
   scoping.  This is because the expansion of a macro may depend on the
   lexical environment at the time the expansion is done.  The failing case
   is where the expansion of one macro contains a macro invocation of a
   second macro.  See below:

   (defmacro m1 ()
     `(m2))

   (defmacro m2 (&environment env)
     (macroexpand '(m3) env))

   (defun f1 ()
     (macrolet ((m3 () '1))
       (m1)))

   (defun f2 ()
     (macrolet ((m3 () '2))
       (m1)))

   Consider what would happen if M2 were a displacing macro.  When F1 was
   compiled, it would splice its expansion ('1) in place of the macro call
   (the quoted constant in M1).  When F2 was compiled, the wrong expansion
   for M2 would be used.

Well geez, this scenario doesn't depend on lexical scoping or
MACROLET.  All it requires is that M1 return a quoted constant call to
M2, and that M2 somehow be able to expand differently at different
times even though it accepts no arguments (e.g. it could gensym, or
receive information via special variables bound by COMPILER-LET).

So instead of displacing in the traditional way by bashing the head
cons of the macro form, Phase I could displace by bashing the car of
the cons that points to the macro form.  It ought to do this anyway
because macros can expand to atoms.  The traditional approach was
necessitated only, of course, by the fact that a pointer to the
"parent" cons (the one whose car is the macro form) is not available
to the macro body code.

Seems to me this *has* to be okay because nothing in CLtL specifies
that macros must be expanded more than once during compilation, nor
does it specify *when* they get expanded, except of course that the
rules for MACROLET must be followed.

-- Scott