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

DEFCONSTANT



A key question concerning DEFCONSTANT is whether two uses of the same
constant must produce values that are EQ.  Consider the following silly
code fragment:

(defconstant foo '(rabid wombat))
(defun bar () foo)
(defun bletch () foo)
(defun urgh () (eq (bar) (bletch)))

Compile this and call URGH.  If DEFCONSTANT has to work EXACTLY like
load-time SETQ, then URGH is guaranteed to return T.  This is the
intuitive thing.  However, if we require this, then it would take a very
wily compiler indeed to ever do any compile-time substitution.  Even if
the URGH function were not present in the compiled file, the user could
come along and write it at runtime.  References that get consumed within
a compiled function and not passed on out of it would be OK, but this is
a new class of things to worry about.  Adding something to a constant
consumes it, but consing it into a list does not -- someone could still
check for EQ on the constituent.

Under these rules, our compiler would substitute for constant fixnum
values, characters, symbols, and nothing else.  The key question is
whether the init form does any consing without "consuming" the result;
we would just be lazy and panic at the first sight of a cons or unknown
form.  (We'd probably try to find a loophole by which we could include
byte specifiers, but this is tricky since we use CONSes for them right
now.)  Note that if we replace '(rabid wombat) with 3.1415L0, in an
implementation where this number is consed, we cannot do the
substitution either, as long as URGH is supposed to return T.

The above "intuitive" rules allow up about 90% of the places where you'd
really like compile-time substitution -- fixnums are by far the most
important case -- but leaves out constant lists, simple strings, and
consed numbers.  These could still appear in DEFCONSTANT, but you'd have
to do a special-variable reference to get at them.

We could instead state in the manual that two references to a constant
symbol are not guaranteed to be EQ, and if you want EQ values you should
use DEFVAR or DEFPARAMETER.  This could be explained as a sort of macro:
references to the symbol in compiled code are replaced (or can be
replaced) with the init form, quoted.  The init form is also evaluated
at load time and the constant is bound to the result, so
special-variable references to it at runtime work properly.

I slightly favor the latter approach, since it greatly increases the
utility of DEFCONSTANT and allows for all sorts of optimizations to
occur.  However, it is non-intuitive for the average user.  I could go
either way on this.  I think that some of the implications for program
verifiers and the like could be pretty deep -- referential transparency
and all of that.

Opinions?

-- Scott