Message from discussion
Constants and DEFCONSTANT
From: Vassil Nikolov <vniko...@poboxes.com>
Subject: Re: Constants and DEFCONSTANT
Date: 1999/04/03
Message-ID: <7e3mod$cn5$1@nnrp1.dejanews.com>
X-Deja-AN: 462107146
References: <nkjpv5y244f.fsf@tfeb.org> <sfwk8w5eu91.fsf@world.std.com> <ixhfr4njna.fsf@gaspode.cam.harlequin.co.uk> <sfw3e2nbwmu.fsf@world.std.com> <7dr23c$2re$1@shell5.ba.best.com> <7dro3d$fja$1@nnrp1.dejanews.com> <sfwpv5q8imr.fsf@world.std.com> <7dv9jq$in9$1@nnrp1.dejanews.com> <sfwzp4s2cqk.fsf@world.std.com>
X-Http-Proxy: 1.0 sonata.einet.bg:8080 (Squid/2.2.PRE2), 1.0 x9.dejanews.com:80 (Squid/1.1.22) for client 195.138.129.51, 195.138.129.1
Organization: Deja News - The Leader in Internet Discussion
X-Article-Creation-Date: Sat Apr 03 00:18:27 1999 GMT
Newsgroups: comp.lang.lisp
X-Http-User-Agent: Mozilla/2.02 (Macintosh; I; 68K)
In article <sfwzp4s2cqk....@world.std.com>,
Kent M Pitman <pit...@world.std.com> wrote:
(...)
> As with my equality paper, it is an intentional issue just how far down
> identity goes.
(...) ;can one get the same object back?
I knew that this issue is going to teach me more than just about
constants and constantness. I can see more clearly now that
my mindset about objects goes like this: the identity of an
object lasts only as long as it lives in core, i.e. there is
a `live' reference to it. Externalising and internalising
in the general case means losing its identity. There is no
_general_ way to prevent this: achieving this prevention can only
be considered on a case per case basis.
(I am not saying that my mindset is right or wrong, just want to
grasp it better. And it does need more refinement.)
(...) ;on language design
You are right that I don't think like a language designer, and I
should try to imagine myself in a language designer's shoes, as it
were, more often. I guess I am now rather irresponsibly testing
how far the cracks would open...
As an aside, isn't one of the good things about Lisp that with it
one plays with a _language_, while with C one plays with a _machine_
(and with C++ one---well, is engaged in non-playful activities with
a machine).
> I'll give you an example that is off-topic
(...) ;an instructive example so what if it might be off-topic
> I had code that said:
> (member x '(a $make-compiler-happy$))
> because it was constructed by other code that did
> `(member x '(,@things $make-compiler-happy$))
> and I did not know how many things I would be given.
(...)
I think that this is at least one of the reasons why it is important
to distinguish between program writing time and compile time.
(Or should it be `program write time'?)
(...)
> > (defconstant =foo= (make-thing) "some object")
>
> (Confess: This whole discussion was just an excuse to slip in some
> subliminal references to =...= for constants, wasn't it? :-)
:-)
You got me!
<annoying-Latin-mode-again>
Next I'll be putting into my signature `Ceterum, censeo, names of
constant variables should have equal signs around them.'
</annoying-Latin-mode-again>
(...) ;number of times a DEFCONSTANT initialisation form is evaluated
; vs. number of times a LOAD-TIME-VALUE form in a symbol macro
; expansion is evaluated
> I don't get the thing with the files. Files are an artificial concept
> that you shouldn't introduce unless you absolutely have to. If you want
> to talk about files with respect to externalization, there may be
> no alternative. But "evaluation" is not about externalization, and indeed
> evaluation may be insensitive to what file is involved (for
> nested files, for cases where no file is involved, etc.).
Trying to get this right: `artificial' in the sense that if a program
consists of several files, an equivalent program can be constructed
from combining all files into one? But doesn't such equivalence have
only theoretical value?
I may be missing the point again but the concept of files has a clear
presence via LOAD and COMPILE-FILE. My concern here is related not to
externalisation but to side effects (including functions such as RANDOM
that return different values on each invocation), and I wanted to
highlight differences I can see between DEFCONSTANT and
DEFINE-SYMBOL-MACRO (straighforward use of the latter in particular,
imitating #define).
As far as I can see,
(defconstant =foo= '(a b c) "a list")
and
(define-symbol-macro =foo= '(a b c))
do the same job; but consider these two scenarios (untested, sorry!):
(i) DEFCONSTANT:
;;; setting the stage:
(defparameter *counter* 0 "to be incremented by one")
(defun counter () (list 'counter (incf *counter*)))
(defconstant =foo-const= (load-time-value (counter) t)
"a list containing an integer")
File one:
(format t "~&one/1: ~S" =foo-const=)
(format t "~&one/2: ~S" =foo-const=)
File two:
(format t "~&two/1: ~S" =foo-const=)
(format t "~&two/2: ~S" =foo-const=)
Whether constant variables are inlined or not, loading these files
one after the other would print
one/1: (COUNTER 1)
one/2: (COUNTER 1)
two/1: (COUNTER 1)
two/2: (COUNTER 1)
(ii) DEFINE-SYMBOL-MACRO:
;;; setting the stage:
(defparameter *counter* 0 "to be incremented by one")
(defun counter () (list 'counter (incf *counter*)))
(define-symbol-macro =foo-macro= (load-time-value (counter) t))
File one:
(format t "~&one/1: ~S" =foo-macro=)
(format t "~&one/2: ~S" =foo-macro=)
File two:
(format t "~&two/1: ~S" =foo-macro=)
(format t "~&two/2: ~S" =foo-macro=)
And the printout would be:
one/1: (COUNTER 1)
one/2: (COUNTER 1) _or_ (COUNTER 2)
two/1: (COUNTER 2) _or_ (COUNTER 3)
two/2: (COUNTER 2) _or_ (COUNTER 4)
> > (2) DEFCONSTANT where the value is interned in some way, e.g.
> >
> > (defconstant =bar= 'baz "an interned symbol")
> >
> > EQL identity is guaranteed not by virtue of what DEFCONSTANT does
> > but by virtue of the properties of the value given to the constant
> > variable: (EQL =BAR= =BAR=) and (EQL =BAR= 'BAZ) will both be true
> > because this is how interned symbols work, and it doesn't matter
> > if =BAR= is inlined or not. The reference is not registered,
> > the value is.
> >
> > (By the way, this reminds me of the concept of otherwise accessible
> > parts.)
>
> I just knew someone was going to raise that. It seems like it should
> be related, but it's not. Indeed, just the opposite. The whole PROBLEM
Problem?
> is that the parts of a constant ARE accessible. Why? Because the whole
Yes, they are---this is what I wrote---`otherwise accessible,' not
`otherwise inaccessible' (was it too self-assured of me to coin the
opposite of the existing phrase?).
> point of a constant is to use it in code. If you didn't use it, you
> wouldn't need it. And having used it, you can't know what the system
> has done with it--unless you forbid the system from doing useful things,
> which seems silly.
God forbid that I should forbid anything or anyone from doing useful
things... (not that I don't do silly things from time to time).
> > (3) A constant reference where the value is not interned in any way:
> > EQL identity is guaranteed only via the same reference but not between
> > the constant reference and the value produced by another evaluation
> > of the initialisation form. The reference is registered (terminology?),
> > the value isn't.
>
> I'm not sure what it means to be the same reference.
> In (let* ((x +quux+) (y x)) (eql x y)) is there one reference or two?
^^^^^^ ^ ^ ^
Four occurrences of the same reference which has been propagated
from +QUUX+ to X and then via X to Y.
Yes, there's some ambiguity in `reference.' If we agree to visualise
references as arrows, sometimes `reference' means the start of the
arrow and sometimes the end of the arrow, I think. So in the above
LET* form there are three arrows (one for +QUUX+, one for X, one
for Y), with X's being used twice, and all of which end at the same
object.
(...)
> > Correct me if I am wrong, but inlining may happen no matter
> > if the initialisation form in DEFCONSTANT is QUOTE or LOAD-TIME-VALUE.
>
> I'm not positive I know the answer or that there is an answer.
> I can see differing interpretations.
(...)
> I know I've spent so long replying to this that
> I'm not going to spend more time researching it today.
Yes, perhaps in the spirit of lazy evaluation this question is better
left until someone really *must* know, and then they will have a
specific case in hand which might make arguing one way or the other
easier.
> > > > Another thing that might be useful would be control over the
> > > > mutability of the object that is the value of a constant (similar
> > > > to what is there for LOAD-TIME-VALUE).
> > >
> > > I don't know what this is about. LOAD-TIME-VALUE seems entirely
> > > powerful enough.
> >
> > Once again, please read `constant variable' for `constant' in my
> > sentence. A constant variable is always immutable. (There is
> > no `read-only-p' parameter to DEFCONSTANT.)
>
> It's not obvious to me what it would mean. Suppose I have a non-read-only
> constant and I write it to a file, then I modify it, then I write it to
> another file. What is the world like when I load both files in the target
> environment? The whole point of a thing you can "aggressively reason about"
> is that "you know its contents". It doesn't make sense to say you can know
> its identity in advance because it has no pointer identity until runtime.
> Only its parts matter, so to say that you're going to sacrifice the parts
> for the iddentity so that you can modify it but you can't trust the contents
> of that modification seems very odd.
I admit I wasn't thinking about externalising and then internalising
with modifications in between, just about in-core operations. It's
best for me to retract my idea at least until I know better what I am
talking about.
(...)
> > (And `adequately' here also means access in O(1); implementing
> > somehow a constant reference on top of a `private' global variable
> > does not ensure that because of the possibility of deep binding.)
>
> Oh, this is easy to do:
>
> (defparameter +lexical-unbound+ (make-symbol "UNBOUND"))
>
> (defun lexical-cell (x)
> (or (get x 'lexical-cell)
> (setf (get x 'lexical-cell) (cons x +lexical-unbound+))))
CONS in order to ensure that the first argument to OR is false iff
no value has been given yet, right? (And the choice of the first
argument to CONS is mainly for convenience of debugging and inspecting?)
> (defmacro lexical-ref (x)
> `(cdr (load-time-value (lexical-cell 'x))))
^^
Read `quote comma X,' right?
> (defmacro deflexical (var value)
> `(progn (define-symbol-macro ,var (lexical-ref ,var))
> (setq ,var ,value)
> ',var))
>
> This is the reason that that define-symbol-macro is so useful.
> It allows one to implement a variety of other constructs.
>
(...)
> But I think the code is right. You should be able to do:
>
> (deflexical x 3)
>
> (defun foo () x)
>
> (defun bar (f x) (funcall f x))
>
> (defun baz (x) (declare (special x)) (foo))
>
> (bar #'baz 7) => 3
I tried to follow this example in detiail; while trying to understand
what happens, I expanded the macros and obtained:
(defun lexical-cell (x)
(or (get x 'lexical-cell)
(setf (get x 'lexical-cell) (cons x #:UNBOUND))))
(setf (cdr (load-time-value (lexical-cell 'x))) 3)
(defun foo () (cdr (load-time-value (lexical-cell 'x))))
(defun bar (f x) (funcall f x))
(defun baz (x) (declare (special x)) (foo))
(bar #'baz 7) => 3
Are you using LOAD-TIME-VALUE to get a permanent direct handle on
the p-list entry? If so, then this solution is very instructive,
but I want to look at it again in the daytime after I have cught
up with some of my sleep...
(...)
Happy Easter!
---Vassil.
Vassil Nikolov <vniko...@poboxes.com> www.poboxes.com/vnikolov
(You may want to cc your posting to me if I _have_ to see it.)
LEGEMANVALEMFVTVTVM (Ancient Roman programmers' adage.)
-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own