Re: Is DEFCONSTANT broken?
- From: Kaz Kylheku <kkylheku@xxxxxxxxx>
- Date: Mon, 22 Jun 2009 07:01:51 +0000 (UTC)
On 2009-06-21, Ron Garret <rNOSPAMon@xxxxxxxxxxx> wrote:
Consider the following code:
(eval-when (:load-toplevel :compile-toplevel :execute)
(defconstant foo ...)
(defun foo () foo)
)
Are there any circumstances under which, in an ANSI-compliant
implementation of CL, (equal foo (foo)) can return NIL?
It turns out that there are. I'll describe what those circumstances are
in a moment (makes a good exercise to figure it out on your own). The
question I want to raise is, given that this is the case, is this aspect
of DEFCONSTANT's behavior so at odds with intuition that it can be
considered broken? And if so, what, if anything, should be done about
it?
The circumstnaces under which (equal foo (foo)) can return NIL are the
following:
1. FOO is initialized by a non-idempotent initialization function like
GENSYM or GET-INTERNAL-REAL-TIME.
2. The code is compiled into a fasl file.
The reason this can lead to (equal foo (foo)) being NIL is that the spec
says:
"An implementation may choose to evaluate the value-form at compile
time, load time, OR BOTH." [Emphasis added.]
There is no way to fix this. Defconstants are not like Pascal constants
or C #define preprocessor symbols.
If you want that kind of constant in Lisp, there are things like read-time
evaluation, and define-symbol-macro.
If you compile a file containing defconstant which is used in that file,
fistly, the constant is used for compiling (which is like manifest constants
in some other languages), and secondly, the compiler must arrange for that
constant to become defined when the compiled file is loaded (which is
a dynamic language feature).
In Lisp, there is the notion of object similarity, and the concept is
involved in defining what kinds of objects must be externalizeable and
what that means.
Defconstant could be constrained such that the expression whcih gives a value
to the constant must be required to be an externalizeable object. Then the
compilation semantics could be defined like this: the expression is evaluated
only at compile time. The externalizeable object is substituted wherever the
constant is referenced in the code being compiled. Moreover, the compiled file,
when loaded, constructs an object similar to that one, and defines that
constant with that object as its value.
However, this could be regarded as too limiting. Moreover, programmers could
still write defconstants whose value forms produce non-externalizeable objects
(like functions). The behavior of that would simply be undefined---a situation
no better than now.
It's better to have some freedom in defconstants. The way they work now,
you can given them values which are non-externalizeable, and make it all work.
The "problem" (I put it in scare quotes because I consider it a problem
but reasonable people could disagree -- opening that discussion is the
point of posting this) is that "constant" can mean two different things:
1. A value that the user is not allowed to change.
2. A value that the compiler can assume (but is not obligated to
assume) won't change, but which might nonetheless actually change.
The ANSI spec implicitly uses definition 2. IMHO there is more utility
in definition 1.
But as you can see, there is also less utility in 1, because values that
change may still work as de-facto constants, and this may overcome the
externalizeability problem.
Suppose that I want a function as a constant:
(defconstant +print-func+ #'princ)
How the heck can this work without evaluating the expression (function princ)
at compile time, and re-evaluating it again at load time?
You can't stick the function #'princ itself into the compiled file as an
externalized object!
Yet, by means of evaluating the initializing /expression/ at both compile
time and load time, ta da, we can actualy create a consistent constant that
works like a manifest constant at compile time, while denoting the same object
at run time also, even though that kind of object isn't externalizeable
in object files.
defconstant overcomes the externalizeability problem by externalizing the
source code of the expression, and allowing it to be evaluated in different
situations.
It's up to you to make these consistent, but you have the chance.
We just have the weak constraint that it must be possible to evaluate the
expression at compile time, and that in all contexts it evaluates to the same
value.
This is arguably better than some stricter bondage constraints, like for
instance requiring that the initializing expression must be constant
expression.
.
- Follow-Ups:
- Re: Is DEFCONSTANT broken?
- From: Ron Garret
- Re: Is DEFCONSTANT broken?
- References:
- Is DEFCONSTANT broken?
- From: Ron Garret
- Is DEFCONSTANT broken?
- Prev by Date: Re: Ordered map
- Next by Date: Re: Catch/Throw in tree searching
- Previous by thread: Re: Is DEFCONSTANT broken?
- Next by thread: Re: Is DEFCONSTANT broken?
- Index(es):
Relevant Pages
|
Loading