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

Re: Issue: EVAL-WHEN-NON-TOP-LEVEL (Version 5)



I believe your passion for understanding the nesting behavior to be very
dangerous. First and foremost, the job of EVAL-WHEN should be to have a
contract where you can understand it at any given level. If you write
each level with a proper understanding of things, the nesting behavior
will follow.

I want to support definitions like the following:

 (DEFMACRO DEFMACRO (NAME BVL &BODY FORMS)
   `(PROGN (EVAL-WHEN (COMPILE)
	     (COMPILER::NOTICE-DEFMACRO ',NAME ',BVL ',FORMS))
	   (SETF (MACRO-FUNCTION ',NAME) #'(LAMBDA (FORM ENV) ...))))

 (DEFMACRO DEFUN (NAME BVL &BODY FORMS)
   `(PROGN (EVAL-WHEN (COMPILE)
	     (COMPILER::NOTICE-DEFUN ',NAME ',BVL ',FORMS))
	   (SETF (SYMBOL-FUNCTION ',NAME) #'(LAMBDA ...))))

 (DEFMACRO DEFCLASS (NAME SUPERIORS SLOTS &REST OPTIONS)
   `(PROGN (EVAL-WHEN (COMPILE)
	     (COMPILER::DEFCLASS-1-A ...))
	   (EVAL-WHEN (LOAD)
	     (COMPILER::DEFCLASS-1-B ...))
	   (EVAL-WHEN (EVAL)
	     (COMPILER::DEFCLASS-2 ...))
	   ',NAME))

 or 

 (DEFMACRO DEFCLASS (NAME SUPERIORS SLOTS &REST OPTIONS)
   `(PROGN (EVAL-WHEN (COMPILE EVAL)
	     (COMPILER::DEFCLASS-A ...))
	   (EVAL-WHEN (LOAD EVAL)
	     (COMPILER::DEFCLASS-B ...))
	   ',NAME))

All I have to do in order to write such definitions is to know what each of
the keywords means:

 LOAD says I want code run at top-level when loading.
 COMPILE says I want code run at top-level when compiling.
 EVAL says I want code run in embedded code.

That's all I have to know to -write- an EVAL-WHEN. The rest of the rules
for how to process an EVAL-WHEN don't affect how I -write- an EVAL-WHEN,
they just take care of assuring that other people who wrote an EVAL-WHEN
using those same rules don't have their model interfered with.

For example, it doesn't matter that
  (EVAL-WHEN (COMPILE) (EVAL-WHEN (COMPILE) ...))
doesn't execute anything. The reason is that you probably never really
right this. Probably what really happens is Person 1 writes:
  (DEFMACRO FOO () `(EVAL-WHEN (COMPILE) ...))
and Person 2 writes:
  (EVAL-WHEN (COMPILE) (FOO))
Now all that's important is that Person 1 has asked himself the question
"Should FOO do anything when in a non-top-level position?" If he has answered
"Yes" to that, then he has made an extremely obvious programming error, and
no complicated reasoning about nested EVAL-WHEN's is needed to notice it.
If he has answered "No", then he presumably has a good reason. Then we have
to ask, did the person implementing "FOO" document that the form was legitimate
anywhere or only at top-level or what? If he documented that FOO was appropriate
to use anywhere, and he still used only the COMPILE option, then he must have 
somehow proven to himself that the contract of FOO could be satisfied without
any work in some circumstances. Now the only remaining question is, did the
user of FOO read and use that documentation properly. Since we've agreed that
the doc says he could use it anywhere, then regardless of what 
  (EVAL-WHEN (COMPILE) (EVAL-WHEN (COMPILE) ...))
-does-, we know that it is doing what is intended.

A computer linguist at MIT, Bill Martin, once said in class that the
purpose of language syntax is not to enable you to say what was
plausible but to say what is not. If all you wanted to say was the things
that were obvious, you'd just write down a lot of words and assume that they
had their obvious relation. "FOOD EAT JOHN" would mean "John eats food"
and not "Food eats John." We have syntax rules to allow you to say the 
latter just in case that's what you really meant.

The rules we've suggested produce nice nesting behaviors for things like
the following. I would say that in my experience, none of these things are
commonplace, yet they are all legitimate things to want to say. I think the
rules we've offered (and the definitions I've offered above which use those
rules) lead to -extremely- intuitive readings of all of these expressions...

 (DEFUN FOO (X) (DEFUN BAR (Y) (+ X Y)))

 defines a function FOO which when called defines a function BAR which
 adds the value that was given to FOO to the value that was given to BAR.

 (DEFUN FOO (X) (DEFMACRO BAR (Y) `'(FOOBAR ,X ,Y)))

 defines a function FOO which when called defines a macro BAR which
 returns a list of the three things: FOOBAR and the original arguments
 to FOO and BAR.

 (LET ((SOMETHING (LIST A B C)))
   (DEFMACRO ZAP () `',SOMETHING))

 defines a macro ZAP which returns the value SOMETHING. This macro has no
 effect at compile time (although it can be subsequently loaded into the
 compiler) because the compiler cannot know what the runtime value of
 SOMETHING is.

 (EVAL-WHEN (EVAL COMPILE LOAD)
   (LET ((SOMETHING (LIST A B C)))
     (DEFMACRO ZAP () `',SOMETHING)))

 is like above but defines the macro in a way that is also visible to the
 compiler.

I have to say that I find all of these nesting behaviors to be -extremely-
intuitive (in addition to being really the only acceptable ones). They
follow quite naturally from our definition of EVAL-WHEN and not from any
previous suggestion of how to rationalize EVAL-WHEN that I've seen.