> From: Jeff Dalton <aiai.ed.ac.uk at jeff>
>
> Subject: Re: Bug in macrolet?
> To: Steve Haflich <franz.com at smh>, Antonio Leitao <gia.ist.utl.pt at aml>
> Cc: <cs.berkeley.edu at allegro-cl>
>
> Steve Haflich <franz.com at smh> wrote:
>
> I would certainly *expect* an error when both running interpreted
> and when the code is compiled.
>
> This "expect" concept isn't appropriate when analyzing what a standard
> says.
Here's a thought: at that point in my message, I wasn't analyzing
what the standard said.
Why is it that so often, on the net, people -- even very reasonable
people, and Steve is certainly that -- go stright for the "he said
something stupid" interpretation, when alternatives are available?
[Yes I know inappropriate doesn't equal stupid, but still...
And, hey, I know I do it too. There's something about the net
that seems to encourage a lack of "charity of interpretation",
as it's sometimes called by philosophers.]
> [...] Now let's see if we can deal with the details.
>
> The ANS says that the results are undefined if reference is made to a
> lexically-defined function of variable. (The ANS elsewhere gives
> reference to block names the same status.) But it does _not_ say that
> the macrolet body does not see surrounding definitions. Indeed, the
> implication of the text is that it _does_ see them. Here is an
> example in silly but conforming code:
>
> (defmacro with-breakpoint (func &rest subforms &environment e)
> (let ((form (macroexpand `(,func <subforms) at ,> e)))
> `(progn (when *breakpoint* (break "Breakpoint: ~s" ',form))
> ,form)))
>
> (defun foo (a)
> (macrolet ((bar (z) (* z z)))
> (flet ((bar (x) (1+ (bar x))))
> (with-breakpoint (bar a)))))
Ok, that has to be rewritten a bit to avoid some errors, giving
(defmacro with-breakpoint ((func &rest subforms) &environment e)
(let ((form (macroexpand `(,func <subforms) at ,> e)))
`(progn (when *breakpoint* (break "Breakpoint: ~s" ',form))
,form)))
(defun foo (a)
(macrolet ((bar (z) `(* ,z ,z)))
(flet ((bar (x) (1+ (bar x))))
(with-breakpoint (bar a)))))
And that seems to work pretty much as I'd expect.
[I include the rewritten code above just so people can try it out,
if they want to, not because I think it's important to mention the
errors.]
> It is very clear that when the compiler compiles the innermost call to
> bar, either the flet bar must be visible in the lexical environment in
> order to shadow the macrolet, or else the environment at that point
> must somehow have the macrolet removed from it. To me it seems
> implementationally simpler just to leave the flet in the environment.
There are two questions: (1) does the compiler know what names are
lexically visible/shadowed? (which is clearly something it could
know at compile-time) and (2) does the compiler know the values the
lexically-scoped names will have? (which cannot in general be known
at compile-time). The macrolet/flet example above appears to be most
relevant to (1), but the question re Allegro's behaviour seemed
to be more about (2).
> In summary, and IMO, the violation of expectations comes about because
> CL is not congruent in these two things. CL more or less defines the
> _defined_ semantics in such a way that macroexpand time is distinct
> from execution time, but the lexical name shadowing of both function
> and variable namespaces merges both macroexpand-time names and
> execution-time names. This is fundamentally illogical, but it derives
> from the history of Lisp as an interpreted language where these two
> times were not deparate -- fsubrs and all that.
I don't think it's quite that bad.
It's natural for macros and functions to be in the same namespace,
given that calls to both are written (name-of-fn-or-macro arg*). It
would be quite confusing if they weren't, and it's not even clear what
that would mean (how would the implementation know whether it was a
function or macro that should be called?), unless functions and macros
were not allowed to have the same names, which is pretty close to
having them in the same namespace anyway. (Saying a macro definition
always takes precedence over a function definition would also be
pretty much the same as having them in the same namespace, because
otherwise why would it be *function* names that were affected?)
Macrolet is supposed to provide local macros, lexically scoped.
So if macros and functions are in the same namespace, it's not
at all weird for them to be able to shadow each other. Indeed,
it could be quite confusing if they couldn't.
Moreover, the same issues would arise even if macros were not defined
in Lisp (e.g if some special pattern language was used, as in Dylan)
so that macroexpansion and compilation could be performed without any
Lisp runtime activity at all.
Steve seems to be suggesting that Common Lisp has a "fundamentally
illogical" merging of macroexpand-time names and execution-time
names, and that this is due to an interpreter-based past.
I would say instead that what CL does makes good sense from a
*compiler's* point of view. All of the names in question are names
available at compile time, and the shadowing can be performed at
compile-time. Moreover, this would be so, given a suitable
macro-definition language, so that macros weren't defined in Lisp,
even if the compiler were a separate program, perhaps not even written
in Lisp, that merely translated Lisp source code into, say, assembler,
so that there was no question of any mingling of compile-time (which
would include macroexpand-time) and run-time.
[If macros were defined in Lisp, or the compiler was written in Lisp,
there could still be complete separation of compile-time (including
Lisp run-time during the compilation process) and application
run-time.]
Moreover, it's a normal feature of compilation that some names are
compiled away. The names of local variables, functions, macros, and
so on, could all be compiled away. And what we're talking about here
is what happens regarding local definitions. So we could regard these
names as compile-time names that are not there at all at run-time.