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


References:	CLtL p. 69-70
Edit History:   6-May-88, V1 by Sandra Loosemore

Problem Description:

The current description of how the compiler should handle EVAL-WHEN
only makes sense when it appears as a top-level form in the file being
compiled.  Other proposals being considered by the compiler cleanup
committee require that we clarify how the compiler should process
EVAL-WHENs appearing at non-top-level, such as within LET or FUNCTION
special forms. 


There are three possible processing situations which may be specified
in EVAL-WHEN.  The interpreter pays attention to only the EVAL
situation, while the LOAD and COMPILE situations are handled by the

The EVAL situation corresponds to the normal processing of the
interpreter.  If EVAL is specified, the interpreter evaluates each
form in the body of the EVAL-WHEN as an implicit PROGN, using the
current lexical environment.  If the EVAL situation is not specified,
then the interpreter must return NIL as the value of the EVAL-WHEN
form, without evaluating the body. 

The LOAD situation corresponds to the normal processing of the
compiler.  To the compiler, (EVAL-WHEN (LOAD) ...) is equivalent to a
PROGN form; the compiler simply arranges for the body to be
``evaluated'' in the lexical environment in which the EVAL-WHEN form
appears.  If the LOAD situation is not specified, the compiler
arranges for the EVAL-WHEN form to return a value of NIL without
evaluating the body forms.  (The name LOAD is something of a misnomer,
because ``evaluation'' happens not at LOAD time, but when the
EVAL-WHEN form would normally be ``evaluated''.  For example, if an
EVAL-WHEN form appears in the body of a DEFUN, it is ``evaluated''
when that function is called.)

The EVAL and LOAD situations are therefore quite similar and
correspond to normal evaluation semantics.  That is, one could
consider that each form and nested subform is implicitly wrapped with
an (EVAL-WHEN (EVAL LOAD) ...). 

The COMPILE situation indicates that the compiler itself should
evaluate the body of the EVAL-WHEN form as an implicit PROGN in the
null lexical environment.  During the evaluation, if a nested
EVAL-WHEN appears in the body, the interpreter follows its usual rule
of checking only whether or not the EVAL situation is specified to
decide whether or not the body of the nested EVAL-WHEN should be

If both the COMPILE and LOAD situations are specified, the compiler
first performs the evaluation for the COMPILE situation.  Then, the
normal processing for the LOAD situation takes place, except that the
compile-time evaluation of nested (EVAL-WHEN (COMPILE) ...) forms in
the body is suppressed, preventing repeated evaluations of subforms.

(EVAL-WHEN (COMPILE) ...) should be used with caution in non-top-level
situations.  For example, if the following appears as a top level form
in a file being compiled

    (let ((x  (some-hairy-computation)))
        (eval-when (eval compile load) (print x)))

the variable X will be treated as special during the compile-time
evaluation, and the value printed will be its symbol-value.  To
guarantee consistency between compile-time evaluation and the normal
processing, one should wrap the entire top-level form in an EVAL-WHEN,
as follows:

    (eval-when (eval-compile load)
        (let ((x  (some-hairy-computation)))
	    (print x)))


The behavior of top-level EVAL-WHENs as specified in this proposal
remains almost identical to that specified in CLtL.  The major
addition is specifying the lexical environment in which non-top-level
EVAL-WHENs are processed.  It is clear that the COMPILE situation must
always be processed in the null lexical environment, since the actual
lexical environment is not available at compile time.  Having the EVAL
and LOAD situations evaluate in the proper environment leads to
differing semantics, but it appears to be the behavior that most
people expect.

Suppression of COMPILE evaluations in nested EVAL-WHENs is necessary
to achieve certain desirable behaviors, such as the macro example in
section 4 of the DEFINING-MACROS-NON-TOP-LEVEL proposal.

Current Practice:

Cost to implementors:

Probably fairly minor in most implementations.  

As an implementation technique, we suggest implementing EVAL-WHEN as a 
macro which uses a state variable (rebound by EVAL, COMPILE, and
COMPILE-FILE) to keep track of the current context.

    (defmacro eval-when (situations &body body)
        (cond ((null *compiling-p*)
               (if (member 'eval situations)
                   `(progn ,@body)
              ((and (member 'compile situations) (member 'load situations))
               (eval `(progn ,@body))
               `(macrolet ((eval-when (situations &body body)
                               (if (member 'load situations)
                                   `(progn ,@body)
              ((member 'compile situations)
               (eval `(progn ,@body))
              ((member 'load situations)
               `(progn ,@body))
              (t 'nil)))

Cost to users:

Since CLtL does not currently specify what the meaning of EVAL-WHEN
forms at non-top-level is, existing code which depends on their use is
already nonportable.  Preventing repeated evaluations of subforms when
EVAL-WHENs are nested is unlikely to cause any serious compatibility
problems, since the current model would already result in only a
single evaluation in the case when the code is processed


Clarifying the meaning of EVAL-WHEN allows the behavior of defining
macros such as DEFMACRO to be specified in terms of EVAL-WHEN.  As a
side effect, it would then become meaningful for defining macros to
appear at other than top-level. 


This proposal reflects what appears to be the consensus of the
compiler cleanup committee on this issue.