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

Re: Macrolet

> From: bha <@ada3.ca.boeing.com:bha@gumby.boeing.com>
> I am trying to use MACROLET to hide the global definition of a macro
> name with my own version.  I want to define a function that accepts a
> form to be evaluated in the lexical environment of the MACROLET.
> I know that I must have to deal with the environment argument to
> macroexpand-1 but can't seem to grok it.  The following never seems to
> expand the form correctly.
> (defun foo (form)
>   (macrolet ((my-macro (&rest x)
> 		 `(funcall #'+ ,@x)))
>     (format t "~&Form: ~a~%Expansion: ~a~&" form (macroexpand-1 form))
>     (eval (macroexpand-1 form))
>     ))
> => (foo '(my-macro 1 2 3))
> This should return 6 but the eval causes an undefined function error
> since the expansion is never really done.
> So, how do I get macroexpand-1 to recognize the lexical environment
> that the my-macro macro is defined in?

There is a misconception here about how and when lexical analysis
(which includes macroexpansion) gets done.  Conceptually,
macroexpansion and the accompanying analysis of forms within lexical
environments are done when forms are promoted into functions.  There
is some latitude when this happens, but the language standard requires
that it happen no later than when a function is COMPILEd or
COMPILE-FILEd, and allows it to happen immediately when the FUNCTION
special form is processed by EVAL.  (One important time when this
happens is when the intepreter executes a DEFUN.)  Indeed this is
typically done in "compiler-only" implementations.

The important point is that once a form has been lexically analyzed
and converted into functionhood (or "promoted" or "closed over",
depending on the literature) the lexical environment is discarded.
The task of lexical analysis is precisely to process and eliminate
MACROLET, SYMBOL-MACROLET, DECLARE, and other lexical constructs.
Conceptualy, lexical analysis happens "all at once" whenever forms are
promoted into functions.  The body of forms that are analyzed together
are: a single top-level form in a file being compiled; a lambda
expression that is the second argument to COMPILE or the first
argument in a COERCE to type FUNCTION; and the argument for to EVAL.
The language is defined this way because if it were not true that
lexical analysis happens all at once, effective compilation of the
lexical language would be impossible.  Once a lexical body of forms
has been analyzed into code, it is in general impossible to inject any
new forms into that body.

Your function is trying to preserve the lexical construct that exists
at the time of its definition and use them at the time of its
execution.  There are hideous kludges to do what you're trying to do
in an implementation that has a real interpreter, provided you're
willing not to compile FOO, but they will fail in other compiler-only
implementations.  Probably a clearer way to implement what you're
trying to do is something like this:

   <1> (defun foo (form)
	 (eval `(macrolet ((my-macro (&rest x)
			     `(funcall #'+ ,@x)))
   <2> (foo '(my-macro 1 2 3))

This will work portably both interpreted and compiled.  The idea is
that both the MACROLET and its expansion occur in the same lexical
body of code (the argument to EVAL) and so undergo lexical processing