From: Rob Warnock on
Kaz Kylheku <kkylheku(a)gmail.com> wrote:
+---------------
| On 2009-10-25, Frank GOENNINGER <dg1sbg(a)googlemail.com> wrote:
| The LOAD-TIME-VALUE form essentially does nothing special in code
| which isn't compiled. Whenever it is evaluated, its enclosed form
| is evaluated whenever it is.
+---------------

Well, close. It's implementation-dependent whether EVAL evaluates it
once or more than once:

Special Operator LOAD-TIME-VALUE
...
If the same list (load-time-value form) is evaluated or compiled
more than once, it is implementation-dependent whether form is
evaluated only once or is evaluated more than once.

+---------------
| The code depends on compilation semantics; it expects the (list 1)
| form to be evaluated once at compile time and the result ``baked''
| into the code, so that each call to the closure will be working
| with the same list object as the previous call.
+---------------

Again, close. When compiled with COMPILE-FILE, the form is *NOT*
evaluated at compile time, but at load time!

If a load-time-value expression is processed by compile-file,
the compiler performs its normal semantic processing (such as
macro expansion and translation into machine code) on 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 a literal object at run time.

But when compiled with COMPILE, it *is* evaluated at compile time:

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 a literal object in the compiled code.

And in all three cases, "FORM is evaluated in a null lexical environment",
which might produce unexpected results, e.g.:

> (define-symbol-macro x 123) ; Something that can be lexically re-bound

X
> (defun foo ()
(let* ((x 456)
(y (1+ x))
(z (load-time-value (1+ x))))
(list :x x :y y :z z)))
FOO
> (compile *)
; Compiling LAMBDA NIL:
; Compiling Top-Level Form:

FOO
NIL
NIL
cmu> (foo)

(:X 456 :Y 457 :Z 124)
>

This can be especially surprising when *NOT* compiled! ;-}

> (let* ((x 555)
(y (+ 2 x))
(z (load-time-value (+ 2 x))))
(list :x x :y y :z z))

(:X 555 :Y 557 :Z 125)
>

+---------------
| The code is not portable unless compile is applied, because only then is
| the impelmentation required to apply the rules of minimal compilation,
| which include settling the evaluation of load-time-value forms.
+---------------

The "only once" versus "many times" only applies in the EVAL case,
though again, the COMPILE and COMPILE-FILE cases still differ on
*when* the "only once" evaluation is done!

[And as others have already mentioned, the ordering of the evaluation
of such forms with other top-level forms is implementation-dependent: ]

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
implementation-dependent.

[Except that of course evaluation must precede use!]

+---------------
| If compilation is not explicitly requested, it can happen anyway.
| We have implementations that compile everything, including forms
| at the REPL.
+---------------

Yup:

Implementations that implicitly compile (or partially compile)
expressions processed by eval might evaluate form only once,
at the time this compilation is performed.

[E.g., even for interpreted code CMUCL does it only once,
when the code is "converted" from s-exprs to tree code.
(Macros are expanded, once, at the same time.)]

And then we've got that whole "literal object" versus "constant"
thing going on. Everywhere else in CL that I can think of, it is
"unspecified" [nose demons, WW3, etc.] what happens if you try to
modify a literal object, but in this one case:

Read-only-p designates whether the result can be considered a
constant object. If t, the result is a read-only quantity that
can, if appropriate to the implementation, be copied into read-only
space and/or coalesced with similar constant objects from other
programs. If nil (the default), the result must be neither copied
nor coalesced; it must be considered to be potentially modifiable data.

So despite the value of a LOAD-TIME-VALUE form "being treated as a
literal object at run time", the *default* case [absent a :READ-ONLY-P T]
is that "it must be considered to be potentially modifiable data".
Say what?!? "Who ordered *that*?!?" [1] [2]


-Rob

[1] What theoretical physicist Isidor I. Rabi reportedly said
circa 1936 upon hearing of the discovery of the muon.

[2] Other than the CLHS entry for "Special Operator EVAL-WHEN" and the
other pages which elaborate upon it [such as "3.2.3.1 Processing
of Top Level Forms"], the "Special Operator LOAD-TIME-VALUE" is
one of the more baroque parts of the CLHS, IMHO.

-----
Rob Warnock <rpw3(a)rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607