CLHS: Issue LOAD-TIME-EVAL Writeup

#

## Issue LOAD-TIME-EVAL Writeup

    Forum:		Compiler
    Issue:          LOAD-TIME-EVAL
    References:     #, (p. 356),  (EVAL-WHEN (LOAD) ...) (p. 69-70)
    		issue SHARP-COMMA-CONFUSION
    Category:       ADDITION
    Edit history:   06-Jun-87, Version 1 by James Kempf
                    17-Jul-87, Version 2 by James Kempf
                    12-Nov-87, Version 3 by Pitman (alternate direction)
                    01-Feb-88, Version 4 by Moon
                      (from version 2 w/ edits suggested by Masinter)
                    06-Jun-88, Version 5 by Pitman
                      (fairly major overhaul, merging versions 3 and 4)
                    21-Sep-88, Version 6 by Moon (stripped down)
    		17-Oct-88, Version 7 by Loosemore (change direction again)
    		30-Dec-88, Version 8 by Loosemore (tweaks)
    		23-Jan-89, Version 9 by Loosemore (amendments)
    		02-Mar-89, Version 10 by Loosemore (new proposal)
    		11-Mar-89, Version 11 by Loosemore

    Problem description:

     Common Lisp provides reader syntax (#,) which allows the programmer
     to designate that a particular expression within a program is to be
     evaluated early (at load time) but to later be treated as a constant.
     Unfortunately, no access to this capability is available to programs
     which construct other programs without going through the reader.

     Some computations can be deferred until load time by use of EVAL-WHEN,
     but since EVAL-WHEN must occur only at toplevel, and since the nesting
     behavior of EVAL-WHEN is quite unintuitive, EVAL-WHEN is not a general
     solution to the problem of load-time computation of program constants.

     Proposal R**2-NEW-SPECIAL-FORM was approved at the January 1989
     meeting.  After the meeting, some additional suggestions were made that
     have been incorporated into proposal R**3-NEW-SPECIAL-FORM.  The sections
     of the two proposals that differ are marked with change bars in the margin.

    Proposal (LOAD-TIME-EVAL:R**2-NEW-SPECIAL-FORM):

     Add a new special form, LOAD-TIME-VALUE, which has the following
     contract:

       LOAD-TIME-VALUE form &optional read-only-p	[Special Form]

       LOAD-TIME-VALUE provides a mechanism for delaying evaluation of <form>
       until the expression is in the "runtime" environment.

       If a LOAD-TIME-VALUE expression is seen by COMPILE-FILE, the compiler
    |  performs normal semantic processing such as macro expansion but
    |  arranges for the evaluation of <form> to occur at load time in a null
       lexical environment, with the result of this evaluation then being
       treated as an immediate quantity at run time.  It is guaranteed that
       the evaluation of <form> will take place only once when the file is
       loaded, but the order of evaluation with respect to the "evaluation"
       of top-level forms in the file is unspecified.

       If a LOAD-TIME-VALUE expression appears within a function compiled
       with COMPILE, the <form> is evaluated at compile time in a null lexical
       environment.  The result of this compile-time evaluation is treated as
       an immediate quantity in the compiled code.

       In interpreted code, <form> is evaluated (by EVAL) in a null
       lexical environment, and one value is returned.  Implementations which
       implicitly compile (or partially compile) expressions passed to
       EVAL may evaluate the <form> only once, at the time this
       compilation is performed.  This is intentionally similar to the
       freedom which implementations are given for the time of expanding
       macros in interpreted code.

    |  Note that, in interpreted code, there is no guarantee as to when
    |  evaluation of <form> will take place, or the number of times the
    |  evaluation will be performed.  Since successive evaluations of the
    |  same LOAD-TIME-VALUE expression may or may not result in an evaluation
    |  which returns a "fresh" object, destructive side-effects to the
    |  resulting object may or may not persist from one evaluation to the
    |  next.  It is safest to explicitly initialize the object returned by
    |  LOAD-TIME-VALUE, if it is later modified destructively.
    |   Implementations must guarantee that each reference to a
    |  LOAD-TIME-VALUE expression results in at least one evaluation of its
    |  nested <form>.  For example,
    |    (DEFMACRO CONS-SELF (X)
    |        `(CONS ,X ,X))
    |    (CONS-SELF (LOAD-TIME-VALUE (COMPUTE-IT)))
    |  must perform two calls to COMPUTE-IT; although there is only one
    |  unique LOAD-TIME-VALUE expression, there are two distinct references
    |  to it.
    |
    |  In the case of a LOAD-TIME-VALUE form appearing in a quoted expression
    |  passed to EVAL, each call to EVAL must result in a new evaluation of
    |  <form>.  For example,
    |    (DEFVAR X 0)
    |    (DEFUN FOO () (EVAL '(LOAD-TIME-VALUE (INCF X))))
    |  is guaranteed to increment X each time FOO is called, while
    |    (DEFUN FOO () (LOAD-TIME-VALUE (INCF X)))
    |  may cause X to be evaluated only once.

       The READ-ONLY-P argument designates whether the result can be considered
       read-only constant. If NIL (the default), the result must be considered
       ordinary, modifiable data. If T, the result is a read-only quantity
       which may, as appropriate, be copied into read-only space and/or shared
       with other programs. (Because this is a special form, this argument is
       -not- evaluated and only the literal symbols T and NIL are permitted.)

     Rationale:

       LOAD-TIME-VALUE is a special form rather than a function or macro
       because it requires special handling by the compiler.

       Requiring the compiler to perform semantic processing such as macro
       expansion on the nested <form>, rather than delaying all such processing
       until load time, has the advantages that fewer macro libraries may need
       to be available at load time, and that loading may be faster and result
       in less consing due to macroexpansion.  If users really want to delay
       macroexpansion to load time, this can be done with an explicit call to
       EVAL, e.g.

        (LOAD-TIME-VALUE (EVAL '(MY-MACRO)))

       Allowing the same LOAD-TIME-VALUE to cause its nested <form> to be
       evaluated more than once makes simplifies its implementation in
       interpreters which do not perform a preprocessing code walk.  It also
       makes the rules for the time of its processing analogous to those
       for macro expansion.

       This proposal explicitly does -not- tie LOAD-TIME-VALUE to the #,
       read macro.  Doing so would be an incompatible change to the definition
       of #, (which is reliably useful only -inside- quoted structure,
       while LOAD-TIME-VALUE must appear -outside- quoted structure in a
       for-evaluation position).

       The requirement that LOAD-TIME-VALUE expressions be evaluated once per
       reference (rather than once per unique expression) prevents problems
       that could result by performing destructive side-effects on a value
       that is unexpectedly referenced in more than one place.

    Proposal (LOAD-TIME-EVAL:R**3-NEW-SPECIAL-FORM):

     Add a new special form, LOAD-TIME-VALUE, which has the following
     contract:

       LOAD-TIME-VALUE form &optional read-only-p	[Special Form]

       LOAD-TIME-VALUE provides a mechanism for delaying evaluation of <form>
       until the expression is in the "runtime" environment.

       If a LOAD-TIME-VALUE expression is seen by COMPILE-FILE, the compiler
    |  performs its normal semantic processing (such as macro expansion and
    |  translation into machine code) on the form, but arranges for the
    |  execution of <form> to occur at load time in a null
       lexical environment, with the result of this evaluation then being
       treated as an immediate quantity at run time.  It is guaranteed that
       the evaluation of <form> will take place only once when the file is
       loaded, but the order of evaluation with respect to the "evaluation"
       of top-level forms in the file is unspecified.

       If a LOAD-TIME-VALUE expression appears within a function compiled
       with COMPILE, the <form> is evaluated at compile time in a null lexical
       environment.  The result of this compile-time evaluation is treated as
       an immediate quantity in the compiled code.

       In interpreted code, <form> is evaluated (by EVAL) in a null
       lexical environment, and one value is returned.  Implementations which
       implicitly compile (or partially compile) expressions passed to
       EVAL may evaluate the <form> only once, at the time this
       compilation is performed.  This is intentionally similar to the
       freedom which implementations are given for the time of expanding
       macros in interpreted code.

    |  If the same (compared with EQ) list (LOAD-TIME-VALUE <form>) is
    |  evaluated or compiled more than once, it is unspecified whether <form>
    |  is evaluated only once or is evaluated more than once.  This can
    |  happen both when an expression being evaluated or compiled shares
    |  substructure, and when the same expression is passed to EVAL or to
    |  COMPILE multiple times.  Since a LOAD-TIME-VALUE expression may be
    |  referenced in more than one place and may be evaluated multiple times
    |  by the interpreter, it is unspecified whether each execution returns
    |  a "fresh" object or returns the same object as some other execution.
    |  Users must use caution when destructively modifying the resulting
    |  object.
    |
    |  If two lists (LOAD-TIME-VALUE <form>) are EQUAL but not EQ, their
    |  values always come from distinct evaluations of <form>.  Coalescing
    |  of these forms is not permitted.

       The READ-ONLY-P argument designates whether the result can be considered
       read-only constant. If NIL (the default), the result must be considered
       ordinary, modifiable data. If T, the result is a read-only quantity
       which may, as appropriate, be copied into read-only space and/or shared
       with other programs. (Because this is a special form, this argument is
       -not- evaluated and only the literal symbols T and NIL are permitted.)

     Rationale:

       LOAD-TIME-VALUE is a special form rather than a function or macro
       because it requires special handling by the compiler.

       Requiring the compiler to perform semantic processing such as macro
       expansion on the nested <form>, rather than delaying all such processing
       until load time, has the advantages that fewer macro libraries may need
       to be available at load time, and that loading may be faster and result
       in less consing due to macroexpansion.  If users really want to delay
       macroexpansion to load time, this can be done with an explicit call to
       EVAL, e.g.

        (LOAD-TIME-VALUE (EVAL '(MY-MACRO)))

       Allowing the same LOAD-TIME-VALUE to cause its nested <form> to be
       evaluated more than once makes simplifies its implementation in
       interpreters which do not perform a preprocessing code walk.  It also
       makes the rules for the time of its processing analogous to those
       for macro expansion.

       This proposal explicitly does -not- tie LOAD-TIME-VALUE to the #,
       read macro.  Doing so would be an incompatible change to the definition
       of #, (which is reliably useful only -inside- quoted structure,
       while LOAD-TIME-VALUE must appear -outside- quoted structure in a
       for-evaluation position).

       Allowing multiple references to the same LOAD-TIME-VALUE expression
       to result in only one interpretation allows it to be specified more
       cleanly.  It also allows interpreters that do not perform a prepass
       to cache LOAD-TIME-VALUE expressions.

    Current Practice:

       This is an addition to the language and has not yet been implemented.

    Cost to Implementors:

       In compiled code, (LOAD-TIME-VALUE <form>) is similar to
       '#,<form>.  Most implementations can probably make use of the same
       mechanism they use to handle #, to handle LOAD-TIME-VALUE.  Note that
       #, does not currently provide a mechanism for dealing with
       non-read-only-ness.

       Implementing LOAD-TIME-VALUE in the interpreter should be fairly
       straightforward, since one simply needs to evaluate the <form> in the
       null lexical environment.  Implementations that use a preprocessing
       code walk in the interpreter to perform macro expansion could process
       LOAD-TIME-VALUE forms at that time.

       Some code-walkers would have to be taught about this new
       special form. Such changes would likely be trivial.

    Cost to Users:

       Some code-walkers would have to be taught about this new
       special form. Such changes would likely be trivial.

    Benefits:

       Users are given a mechanism that to force evaluation to be delayed
       until load time that does not rely on a feature of the reader.

    Discussion:

       Earlier versions (up to version 7) of this proposal stated that
       all semantic processing of the LOAD-TIME-VALUE form should be postponed
       until load time.

       The semantics of LOAD-TIME-VALUE would be simplified considerably if
       the READ-ONLY-P argument were removed and destructive operations on
       the result of evaluating <form> prohibited.  However, some people feel
       that the ability to destructively modify the value is an essential
       feature to include.

       "Collapsing" of multiple references to the same LOAD-TIME-VALUE
       expression could be allowed for read-only situations, but it seems
       like it would be more confusing to make it legal in some situations
       and not in others.

       A number of other alternatives have been considered on this issue,
       including:

       - A proposal for a new special form that would force evaluation of
         the <form> to happen only once.  This was rejected because of
         implementation difficulties.

       - A proposal to add a function making the "magic cookie" used by #,
         available to user code.  The current proposal does not prevent such
         a function from being added, but this approach appeared to have
         less support than making the hook available as a new special form.

       - A proposal to remove #, entirely (issue SHARP-COMMA-CONFUSION).

       - A suggestion to change the behavior of (EVAL-WHEN (LOAD) ...).

    Kent Pitman says:
       Although I'm willing to take multiple evaluation in the interpreter
       as a compromise position, I would like it mentioned in the discussion
       that this was only an expedient to getting this issue accepted at all,
       and that I'm not really happy about it. I have said that I think a
       number of our lingering problems (with EVAL-WHEN, COMPILER-LET, and
       this -- for example) are due to the presence of interpreters which do
       not do a semantic-prepass at a known time. If I had my way, we would
       require a semantic pre-pass and we would then be able to forbid
       multiple evaluations even in the interpreter.

    Moon and Gray support proposal R**3-NEW-SPECIAL-FORM.

    Pitman also expressed willingness to go along with
    R**3-NEW-SPECIAL-FORM, but was somewhat concerned that coalescing
    LOAD-TIME-VALUE results based on EQ-ness of the LOAD-TIME-VALUE form
    could conceivably lead to trouble down the line. However, since he
    could provide no actual examples to back up that worry, and since the
    majority opinion was that some implementations would find a
    restriction against such coalescing an undue burden, the decision was
    made to just `note the concern' and proceed on.  Sandra Loosemore and
    JonL White concur with this position.

Copyright 1996-2005, LispWorks Ltd. All rights reserved.