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

Issue: LOAD-TIME-EVAL (Version 4)

    Date: 14 Jan 88 20:53 PST
    From: Masinter.pa@Xerox.COM

    The last mail on this topic was 12 November. 

    Jul 23: Jim Kempf's version 2 mailed 

    Nov 11: mail about a few typos and bugs in it which he agreed to adopt.

    Nov 12: Kent Pitman mailed version 3
	    Dave Moon replied.

    Version 2 is incompatible with Version 3. 
    Do we go back to version 2 (#, as currently spec'd, allowed only within constants?)

Yes.  Here's a version 4, which is version 2 with your suggested corrections.
I don't know if this meets Kent's requirements, since I never figured out
precisely what they were.

References:	#, (p. 356),  (EVAL-WHEN (LOAD) ...) (p. 69-70)
Category:	ADDITION
Edit history:	Version 2 submitted 7/17/87, James Kempf.
		Version 3, Pitman, 11/12/87 (alternate direction)
		Version 4, Moon, 2/1/88 (Version 2 with edits suggested by Masinter)

Problem description:

The specification of #, (load time evaluation) in Common Lisp provides 
a means, during compilation, of arranging for the evaluation of a 
quoted or (in some implementations) unquoted form within embedded forms
processed by the reader, when the compiled file is loaded.
Inhibition of processing when a file is loaded into the interpreter
is possible using (EVAL-WHEN (LOAD) ... ). Code which is returned
during macroexpansion, however, is not processed by the reader, so
there is no way to arrange for deferral of processing until compiled
file load time in macroexpanded code.


Add the function MAKE-LOAD-TIME-EVAL:

(MAKE-LOAD-TIME-EVAL <form> <env>) => <object>

<form> is a Lisp form, <env> is an environment of the sort received
by the &ENVIRONMENT argument to a macro, and <object> is the result
of evaluating <form>.

When MAKE-LOAD-TIME-EVAL is called from the interpreter or the COMPILE
function, it simply evaluates <form> in the null lexical environment
and returns its value.  When MAKE-LOAD-TIME-EVAL is called during a
file compilation, the <object> is a special object that is recognized
at load time, when it occurs inside a constant.  At load time,
<form> is evaluated and its value is substituted for <object>.  This
is the same mechanism that the #, reader macro uses.

MAKE-LOAD-TIME-EVAL uses its <env> argument and/or dynamic state to
determine whether it is being called during a file compilation.
Until Common Lisp is modified to specify the semantics of file compilation
more precisely, this is necessarily implementation dependent.

Example 1: 

(defmacro print-software-version (&environment env)
  `(quote ,(make-load-time-eval '(format T "~A~%" (software-version))

When interpreted or processed during invocation of COMPILE, this
macro prints the value of (software-version) at macro expansion
time and expands into (quote nil).  When macroexpanded during a
file compilation, printing is deferred until the compiled file is
loaded, and the constant is still (quote nil).

Example 2:

(defmacro table-of-tables (&rest predicates &environment env)
  `(quote ,(mapcar #'(lambda (predicate)
			    `(make-hash-table :test ',predicate)

(table-of-tables eql equal) expands into
(quote ((eql #<table :test eql>) (equal #<table :test equal>)))
except that when macroexpanded during a file compilation,
the tables are not created until load time.  This example
shows that the <object> returned by make-load-time-eval is
recognized even when it is interior to a quoted constant.


Currently, there is no portable way to arrange for code returned
 from a macro to defer evaluation until load time.

Current practice:

Currently, every version of Common Lisp is required to implement
compiler hooks for #, but, since this is only recognized by the
reader, there is no portable way to achieve the same effect. Users
of Portable CommonLoops are encouraged to implement something similar.

Adoption Cost: 

The cost to implementors will depend on how #, is implemented.
In some implementations, the primitives for implementing 
MAKE-LOAD-TIME-EVAL may already exist, in others, more substantial
changes may be required.

Cost of non-adoption: 

There are numerous possible uses for this facility. Version control
and system building facilities (such as the example) and optimization
of method invocation in the Common Lisp Object System come immediately
to mind. While every implementation of Common Lisp could certainly
provide an implementation specific facility, portability would suffer.


Portability. May make extensions to the Common Lisp Object system
via. the metaobject protocol easier.

Conversion Cost:

Most applications developers should not see much difference.


The proposal fills a hole in the spectrum of alternatives for deferring
evaluation until a compiled file is loaded. Currently, code which is 
read by the reader can arrange for it to be done, as can top level code,
but embedded code cannot.


There is likely to be some controversy about this proposal, since
there is no universally agreed upon formal processing model for
Common Lisp, but only a set of informal rules and various implementations.
Those implementations which have the primitives available should be
able to implement MAKE-LOAD-TIME-EVAL with little change to their
kernels, those which don't may require more extensive modifications.