From: Jeff Dalton

Subject: Re: Bug in macrolet?

Date: 1999-7-7 14:09

> 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.