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

Issue: LET-TOP-LEVEL (version 1)



    Date: Thu, 3 Mar 88 17:51:53 PST
    From: Jon L White <edsel!jonl@labrea.Stanford.EDU>

    re:   (let ((x (big-computation-that-returns-22/7)))
	    (defmacro foo (y) `(list ,x ,y))
	    (defun bar (z) (foo (1+ z))))
	(bar 2) => (22/7 3) if it evaluates to anything, but how could the
	compiler know that?

    This is perhaps a bad example of what you are trying to illustrate, for it
    needlesly violates the "global environment" constraint of CLtL p145.

On the contrary, violation of that constraint was precisely the topic
I was trying to illustrate.  Removal of that constraint was the subject
under discussion.

    Since you are merely trying to show up a flaw in the "pre-processor" style
    of evaluators (that pervasively expand macros before beginning evaluation),

That is not at all what I was trying to do.  You completely missed the
point.  However, some of what you then went on to point out is worth
discussing; see below.

For more on the first point, see the message I just sent to Pavel cc to
CL-Cleanup.

    ....
    On the other hand, if we relenquish the strictures of CLtL p145 (which
    some implementations do), then I don't see any problem with the following 
    example:

    ;;; Beginning of file

	(eval-when (eval compile)
	   (let ((cnt 0))
	     (defmacro do-it-once (x) (incf cnt) `(PRINT ,x))
	     (defmacro total-count () ',cnt))
	)

	(defun foo (x)
	  (do-it-once x) 
	  (do-it-once x)
	  (total-count))

    ;;; End of file

This works because the variable named cnt is only referenced at compile
time.  In a more complex case, such as others suggested earlier, where
some data structure was needed at run time too, this type of approach
would not work, or at least it's not clear that it would work.  After
the problem of the conflict between the lexical environment of the macro
invocation and the lexical environment of the macro definition is resolved,
e.g. with Kohlbecker's scheme or one of the more recent similar schemes,
references to the compile time variable could be incorporated into code
resulting from macro expansion, and we would need to make sure that the
semantics of compilation and loading properly preserved the identity
of that variable binding.

An even weirder case would exist if you had said
(eval-when (eval load compile) ...), which would seem quite
reasonable to most users, and would be necessary if cnt had been
accessed by any defuns (in addition to defmacros).  The eval-when
would evaluate its body twice, once at compile time, and again at
load time, creating two distinct variables named cnt.  Now the
defuns would reference one variable and the defmacros would reference
the other.  The point is that this kind of construction works fine
when interpreted, but does not work when compiled (to a file).  We
want to minimize the semantic differences between those two ways of
executing a program.

    So I'm wondering what is meant by the claim:

      "When you define a macro with a non-null closed-over environment, it is 
       clear that the compiler cannot know how to evaluate the expansion function.
       It is therefore reasonable to assume that the compiler cannot expand uses 
       of that macro during the same compilation that encounters the DEFMACRO form.
       If you want a macro like this, you must put the DEFMACRO in a separate file,
       compile and load that file, and then compile any uses of the macro."

You are correct in (implicitly) pointing out that this claim of Pavel's
should have been limited to the case where the non-null closed-over
environment (really the captured free identifiers) came from a load-time
binding rather than a compile-time binding.  Throwing in an eval-when
compile (and not load) eliminates the issue by eliminating the magic
two-phase evaluation that the compiler does.

    If this claim was intended to be limited to "solitary forms", as opposed
    to files, then the following pathelogical function should be considered:

    (defun tryit () 
      (compiler-let ((cnt 0))
	(compiler-let ((throw-away
			 (progn 
			   (defmacro do-it-once (x) (incf cnt) `(QUOTE ,x))
			   (defmacro total-count () `(QUOTE ,cnt))
			   t)))
	  (defun foo (x)
	    (do-it-once x) 
	    (do-it-once x)
	    (total-count)))))

I don't understand how this is relevant, since compiler-let is an operation
on dynamic variables, not lexical variables.

    I can make some sense of this claim if I make a lot of presumptions about
    the notion of "top-level";  but this is still a murky area ("top-level").

Precisely.  See my message to Pavel cc to CL-Cleanup.