[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
draft of alternate proposal for EVAL-WHEN-NON-TOP-LEVEL
- To: cl-compiler@sail.stanford.edu
- Subject: draft of alternate proposal for EVAL-WHEN-NON-TOP-LEVEL
- From: sandra%defun@cs.utah.edu (Sandra J Loosemore)
- Date: Fri, 24 Feb 89 16:27:57 MST
Here is a draft of an alternate EVAL-WHEN proposal. Basically, what
I have done is to fix the problem with the IGNORE-COMPILE proposal
from version 4 that would have caused multiple evaluations in some
cases, and merge that with the new interpretation of the EVAL
situation from Kent's GENERALIZE-EVAL proposal from version 5.
Note that this proposal meets all of the same goals that were set out
in the version 5 writeup.
Since a number of people were getting hung up on the idea of EVAL-WHEN
not passing through top-level-ness, I have introduced another term,
"unshielded". Avoiding confusion is the only reason why I have not
made "unshielded" and "top-level" be the same (although I personally
think the writeup would be simpler if they were). Note that the
writeup for issue DEFINING-MACROS-NON-TOP-LEVEL would also have to be
changed to use "unshielded" instead of "top-level".
I am open to suggestions on this writeup. If everybody else hates
this and thinks that the the general direction here is totally wrong,
I will shut up and go along with the GENERALIZE-EVAL proposal. But I
personally would like to see something like this included in the
writeup we send out to X3J13 to vote on.
Proposal EVAL-WHEN-NON-TOP-LEVEL:GENERALIZE-EVAL-FIX-NESTING:
Replace the description of EVAL-WHEN with the following:
EVAL-WHEN ({situation}*) {form}* [Special Form]
The body of an EVAL-WHEN form is processed as an implicit PROGN, but
only if the situations listed match the processing context. Each
SITUATION must be a symbol, either COMPILE, LOAD, or EVAL.
The context in which an EVAL-WHEN form is processed is referred to as
either shielded or unshielded. The EVAL situation controls processing
of the body in shielded contexts, and the COMPILE and LOAD situations
together control processing of the body in unshielded contexts.
Forms processed by COMPILE-FILE are considered to be shielded if
they appear at non-top-level or are enclosed within an EVAL-WHEN.
Forms that are processed by the COMPILE and EVAL functions are always
considered to be non-top-level and therefore shielded.
The behavior of EVAL-WHEN may be more precisely understood in terms
of a model of how the file compiler, COMPILE-FILE, processes forms
in a file to be compiled. Successive top-level forms are read from
the file using READ and examined by the file compiler before reading
the next.
* If the form is a macro call, it is expanded and the result is
examined recursively as a top-level form.
* If the form is a PROGN, each of its body forms are sequentially
examined as top-level forms.
* If the form is a COMPILER-LET, MACROLET, or SYMBOL-MACROLET, the file
compiler makes the appropriate bindings and the body forms are
recursively examined as an implicit top-level PROGN with those
bindings in effect.
* If the form is an EVAL-WHEN form and the context is unshielded, then
the file compiler performs two processing steps. First, if the
EVAL-WHEN form specifies the COMPILE situation, then the body of the
EVAL-WHEN is evaluated as an implicit PROGN in the executing
environment of the compiler. Then, if the LOAD situation is
specified, the body is recursively examined as an implicit top-level
PROGN, but in a shielded context.
* In all other cases, the compiler arranges for the form to be executed
when the compiled file is loaded. Any subforms are considered to be
non-top-level forms.
If an EVAL-WHEN form appears in a shielded context and the EVAL
situation is specified, then its body is processed as an implicit
PROGN. If the EVAL situation is not specified, the body is ignored
and the EVAL-WHEN form returns a value of NIL.
Clarifications/Consequences:
The following effects are logical consequences of the above proposal:
* It is never the case that the execution of a single EVAL-WHEN
expression will execute the body code more than once.
* The keyword `EVAL' is a misnomer because execution of
the body need not be done by EVAL. In compiled code, such as
(DEFUN FOO () (EVAL-WHEN (EVAL) (PRINT 'FOO)))
it is permissible (even desirable) for the call to PRINT to be compiled.
* Macros intended for use in top-level forms should arrange for all
side-effects to be done by the forms in the macro expansion.
The macro-expander itself should not do the side-effects.
* Placing a variable binding around an EVAL-WHEN reliably captures the
binding because non-top-level forms are always processed in a
shielded context.
* An outer EVAL-WHEN always shadows an inner EVAL-WHEN. The behavior
of nested EVAL-WHENs is symmetric for all three situations:
An EVAL-WHEN in a shielded context that specifies the EVAL situation
enables normal execution of its body. If the EVAL situation is not
specified, the normal execution of all body forms is prevented, even
nested EVAL-WHENs that do specify the EVAL situation.
An EVAL-WHEN in an unshielded context that specifies the LOAD
situation enables load-time execution of its body. If the LOAD
situation is not specified, the load-time execution of all body forms
is prevented, even nested EVAL-WHENs that do specify the LOAD
situation.
An EVAL-WHEN in an unshielded context that specifies the COMPILE
situation enables compile-time execution of its body. If the COMPILE
situation is not specified, the compile-time execution of all body
forms is prevented, even nested EVAL-WHENs that do specify the COMPILE
situation.
* One possible implementation of EVAL-WHEN is as a macro, assuming
some implementation-dependent way of determining whether an environment
is a top-level or non-top-level environment.
(defmacro eval-when (situations &body body &environment env)
(if (not (top-level-environment-p env))
(expand-shielded-eval-when situations body)
(progn
(if (member 'compile situations)
(mapcar #'eval body))
(if (member 'load situations)
`(macrolet ((eval-when (s &body b)
(expand-shielded-eval-when s b)))
,@body)
nil))
))
(defun expand-shielded-eval-when (situations body)
(if (member 'eval situations)
`(progn ,@body)
nil))
Test Cases:
;; #1: The EVAL-WHEN in this case is in a non-top-level position, so only
;; the EVAL keyword is considered. At compile time, this has no effect.
;; At normal execution time this sets (SYMBOL-FUNCTION 'FOO1) to a
;; function which returns 1.
(LET ((X 1))
(EVAL-WHEN (EVAL LOAD COMPILE)
(SETF (SYMBOL-FUNCTION 'FOO1) #'(LAMBDA () X))))
;; #2: If this expression occurs at the top-level of a file to be compiled,
;; it has BOTH a compile time AND a load-time effect of setting
;; (SYMBOL-FUNCTION 'FOO2) to a function which returns 2.
(EVAL-WHEN (EVAL LOAD COMPILE)
(LET ((X 2))
(EVAL-WHEN (EVAL LOAD COMPILE)
(SETF (SYMBOL-FUNCTION 'FOO2) #'(LAMBDA () X)))))
;; #3: If this expression occurs at the top-level of a file to be compiled,
;; it has BOTH a compile time AND a load-time effect of setting the
;; function cell of FOO3 to a function which returns 3.
(EVAL-WHEN (EVAL LOAD COMPILE)
(SETF (SYMBOL-FUNCTION 'FOO3) #'(LAMBDA () 3)))
;; #4: This always does nothing. It simply returns NIL.
(EVAL-WHEN (COMPILE)
(EVAL-WHEN (COMPILE)
(PRINT 'FOO4)))
;; #5: If this form occurs at top-level of a file to be compiled, FOO5 is
;; printed at compile time. If this form occurs in a non-top-level
;; position, nothing is printed at compile time. Regardless of context,
;; nothing is ever printed at load time or execution time.
(EVAL-WHEN (COMPILE)
(EVAL-WHEN (EVAL)
(PRINT 'FOO5)))
;; #6: The description of EVAL-WHEN in CLtL says that this form will print
;; FOO6 at compile time. The description of EVAL-WHEN in this proposal
;; says it will never print anything.
(EVAL-WHEN (EVAL LOAD)
(EVAL-WHEN (COMPILE)
(PRINT 'FOO6)))
Rationale:
This behavior specified by this proposal is simple and easy to
understand, and extends the behavior of EVAL-WHEN usefully to
non-top-level situations. It is largely compatible with the behavior
of EVAL-WHEN specified in CLtL, except for the situation shown in
example #6 above. Some people find the nesting behavior of EVAL-WHEN
specified in this proposal to be an improvement over that specified
in CLtL.
This gives a useful meaning to EVAL-WHEN that supports useful and
predictable behavior if defining macros are used in a non-top-level
situation.
-------