[LISPWORKS][Common Lisp HyperSpec (TM)] [Previous][Up][Next]


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.


[Starting Points][Contents][Index][Symbols][Glossary][Issues]
Copyright 1996-2005, LispWorks Ltd. All rights reserved.