Joerg Hoehle <firstname.lastname@example.org> wrote:
| email@example.com (Rob Warnock) writes:
| > What *will* be available is the state
| > of *being* a constant [using CONSTANTP], but not the value itself.
| So here they [CLISP & CMUCL] differ.
| Thanks for this precision about cmucl's behaviour. I looked at clisp,
| the CLHS and found clisp consistent as well with CLHS: clisp chooses
| the legal option "defconstant not evaluated at compile time".
Careful with that term "evaluated" -- as I read the CLHS, it's only
the *initial-value* of the DEFCONSTANT for which evaluation at compile
time is "optional". But the DEFCONSTANT expression itself *must* be
"recognized" at compile time (if it's a top-level form):
If a DEFCONSTANT form appears as a top level form, the compiler
must recognize that NAME names a constant variable.
The following kinds of forms are considered constant forms:
* Constant variables, such as keywords, symbols defined by
Common Lisp as constant (such as NIL, T, and PI), and symbols
declared as constant by the user in the indicated environment
using DEFCONSTANT are always considered constant forms and
must be recognized as such by CONSTANTP.
So I don't see where the CLHS gives the compiler "the legal option"
of not processing (DEFCONSTANT NAME INITIAL-VALUE) at least to the
extent of making (EVAL-WHEN (:COMPILE-TOPLEVEL :LOAD-TOPLEVEL :EXECUTE)
(CONSTANTP 'NAME)) ==> T.
| About CONSTANTP + EVAL, I also found CLISP to be consistent: It won't
| yield CONSTANTP -> true during compilation...
And because of the above-quoted CLHS sections, I *think* that's a bug.
| ...so user code will not call EVAL on it -- there's a lot of code
| out there, which does: (if (constantp form) (eval form))
That code is broken, too, unless it's under a feature test for
the implementation being one which evaluates "initial-value" at
| From a user POV, the compiler evaluating DEFCONSTANT during
| compilation seems best (in terms of least surprise). It's explicitly
| allowed by CLHS.
Again, I think you're confusing the issue somewhat by using the
phrase "evaluating DEFCONSTANT". This needs to be broken up into
the two acts of "processing DEFCONSTANT" and "evaluating the
initial-value of a DEFCONSTANT". As I read the CLHS, the former is
*required*; it is only the latter which is optional at compile time.
| But this choice has its quirks w.r.t. EQL-ness and
| redefinition at load-time (as cmucl/sbcl users know).
Yes, quite true. Thank goodness there's LOAD-TIME-VALUE... ;-}
| IMHO it's debatable whether (if (constantp form) (eval form)) should
| be flagged as unportable (even broken) code?? CONSTANTP could then
| return T in clisp.
And is required to, if I'm reading the CLHS correctly.
| But what would you do knowing that, if you can't get to the value?
Exactly. The real problem here is the standard, which gave
DEFCONSTANT a little too much leniency about evaluating its
"initial-value" *without* tying that leniency in a consistent
way to what CONSTANTP is required/allowed to do.
p.s. By the way, my solution for this mess was to use the
following routine instead of CONSTANTP in my macros:
(defun compile-time-constantp (form)
#-cmu (constantp form)
#+cmu (and (constantp form)
(or (keywordp form)
(not (symbolp form)))))
In CMUCL, at least, this still yields T for a useful set of forms:
literal numbers, strings, vectors, quoted lists, and keywords [all of
which have values available at compile time], but not for expressions
such as (+ 1 2) [though the CLHS permits a compiler to recognize the
latter as constant, if it can].
Rob Warnock <firstname.lastname@example.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607