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

proposal LOAD-TIME-EVAL:REVISED-NEW-SPECIAL-FORM



Of the numerous proposals of this ilk, this one seems to me to be the one
most workable.

Following our charter, we should look to Interlisp and see how experience 
has fared with its function LOADTIMECONSTANT [maybe never documented?].
Its semantics were, coincidentally, essentially the same as those you have
spelled out for LOAD-TIME-EVAL.  [By the way, shouldn't the Interlisp 
precedent bias us towards the name LOAD-TIME-CONSTANT rather than towards 
LOAD-TIME--EVAL?].

However, a more serious issue seems to have gotten lost in all the flaming.
At Lucid, we all seem to favor flushing #,.  Who really wants it?  At best,
when someone previously suggested flushing #,  I remember an efficiency 
argument being invoked,  but not being _demonstrated_.

There is a serious flaw in the design of #, and for this reason alone
it should be flushed [explication of the "flaw" is further below.] The 
burden of _proof_ to show that any reasonable program is seriously 
hampered without it is on those who invoke the argument.  As Gabriel 
would say of benchmarking, no raw numbers without theory to explain why, 
and certainly no speculation without hard numbers.

Where are the numbers which prove an inefficiency so grave as to cause
us to tolerate this inherently flawed and misleading construct #,?


The "grave flaw" centers around the common misconception that you
are either:

	(1) reading the source code, for interpretation  -- EVAL situation
        (2) reading it for compilation to a file         -- COMPILE situation
        (3) loading the compiled version of the file     -- LOAD situation

that is, one and only one of the three situations applies.  But as Kent's
little trivial example showed:
     (EVAL-WHEN (EVAL COMPILE LOAD)
       (DEFUN FOO (X) #,`(X ,(SQRT 3))))
you may be reading a piece of code for *** two *** of the situations at 
one time.  You are both EVAL'ing and COMPILE'ing a single solitary form 
at essentially "one and the same time".  [Early versions of PCL tried to 
use some such trick, and lost badly.]

The whole idea of the "magic token" is that at read-time you can somehow
inspect either the dynamic or lexical environment, and decide with of the 
three situations is relevant.  But you can't; you can only know which 
situation is relevant at actual processing time -- not at read time (and 
of course a special form has access to the lexical environment).  Sandra 
put it quite well when countering a proposal to make the LOAD-TIME-EVAL 
thing be a function instead of a special form:

    Date: Wed, 21 Sep 88 15:05:28 MDT
    Subject: Re: Issue: LOAD-TIME-EVAL (Version 6)
    . . . 
    there a[re] more situations in which code is processed than simply
    interpretation and compilation.  It is not an either/or situation.  A
    person here at the UofU, for example, has been working on a portable
    type inference preprocessor.  It reads in code, decorates it with lots
    of type declarations, and writes it out to another file which may then
    either be loaded interpretively or compiled with COMPILE-FILE.  . . . 


The implications of these theoretical arguments are not always easy to
follow, so I have reproduced an example file below that helps one see 
just how the screw-up occurs.  Every implementation I've tried this file
in,  when the source is loaded,  "does the right thing".  But when it is
compiled, they all leave the "magic tokens" in the source code for the 
function 'expose'; so  the value of 'foo', after compilation, has these 
tokens in it rather than the desired structure ((COMPILE A) (COMPILE B)). 
Correspondingly, the value of 'bar'  -- the 'exposure-normality' of 'foo' 
-- is ABNORMAL.

Worse yet, because the definition of 'normally-exposed-p' is a macro that 
gets "snapped" at compile time, the error is propagated to the runtime 
setting of 'bar'.  Namely, when the compiled file is loaded, 'foo'  has 
the right value --  ((LOAD A) (LOAD B)) -- but 'bar' wrongly says that 
the 'exposure-normality' state is ABNORMAL.  [Well, one implementation
got the value of 'foo' wrong too, but, hey, what the heck.]


Take this example and try it.  Look at FOO and BAR after each pass.
You'll dislike it!  Flush #,.



-- JonL --



;;;--------------------------------------------------------------------------

(in-package "USER")

;;; Clean the slate, in case this isn't "first time" processing

(eval-when (eval compile load)
 (fmakunbound 'expose)
 (fmakunbound 'exposure-normality)
 (proclaim '(special markers))
)


;;; Use different marker depending on which situation is relevant.

(eval-when (eval)
  (setq markers '((eval a) (eval b) (eval c)))
)
(eval-when (compile)
  (setq markers '((compile a) (compile b) (compile c)))
)
(eval-when (load)
  (setq markers '((load a) (load b) (load c)))
)


(eval-when (eval compile load)
  (defun expose ()
     `(#,(pop markers) #,(pop markers)))
  (defmacro exposure-normality ()
    (if (member (car (expose)) 
		'((eval a) (compile a) (load a))
		:test #'equal)
	`'normal
	`'abnormal))
)




(eval-when (eval compile load)

(defparameter foo (expose))
(defparameter bar (exposure-normality))

)
;;;--------------------------------------------------------------------------