[comp.lang.lisp] Constants in code

gumby@Cygnus.COM (David Vinayak Wallace) (09/23/90)

   Date: 21 Sep 90 22:09:56 GMT
   From: miller@GEM.cam.nist.gov (Bruce R. Miller)

    Larry Masinter wrote:
   >[As a side note, many learning LISP programmers frequently do
   > encounter self-modifying code and are mystified by it, e.g.,
   >   (let ((var '(a b c)))
   >       ...
   >       (nconc var value))

   Without digging thru  CLtL to  determine its  legality, it  seems pretty
   clear that  it  is  unclear  what  it  SHOULD  do!  And  that  different
   interpreters/compilers  will  make  different  choices.  And  that   the
   Committee probably didn't even want to specify.

   In any  case,  one  could  imagine  a  perfectly legitimate interpretter
   consing the list fresh  every time, giving  a 3rd behavior.   Before you
   groan, consider that this might in fact be the most consistent behavior!
   The (QUOTE (A B C)) form is (conceptually, at least) evaluated each time
   the function is called!  Should (QUOTE (A B C)) sometimes 
   return (A B C FOO)?

The interpretation of either case is straightforward.

Quote returns the object it was handed.  It doesn't try to guess "what
you meant."  After all, the code isn't text; quote just has a pointer
to the car of the list you handed it!

I would be unhappy if quote performed computation each time you used
it!  A very common idiom is to pass a quoted, uninterned symbol around
as a marker in some structure.  (I used to use '(())).  This is the
only way I can get a token I guarantee won't appear in my input
stream!

Anyway, the capability to side-effect constants in storage was not
removed for taste reasons, but efficiency; it permits me as a lisp
implementor to place code into read-only storage (and possibly share
it among processes).

miller@cam.nist.gov (Bruce R. Miller) (09/25/90)

In article <GUMBY.90Sep22150717@Cygnus.COM>, David Vinayak Wallace writes: 
>    Date: 21 Sep 90 22:09:56 GMT
>    From: miller@GEM.cam.nist.gov (Bruce R. Miller)
>     Larry Masinter wrote:
>    >   (let ((var '(a b c)))
>    >       ...
>    >       (nconc var value))
> ...
>    In any  case,  one  could  imagine  a  perfectly legitimate interpretter
>    consing the list fresh  every time, giving  a 3rd behavior.   Before you
>    groan, consider that this might in fact be the most consistent behavior!
>    The (QUOTE (A B C)) form is (conceptually, at least) evaluated each time
>    the function is called!  Should (QUOTE (A B C)) sometimes 
>    return (A B C FOO)?
> 
> The interpretation of either case is straightforward.
> 
> Quote returns the object it was handed.  It doesn't try to guess "what
> you meant."  After all, the code isn't text; quote just has a pointer
> to the car of the list you handed it!
> 
I'm a little confused about what you're saying. In the simplest case,
READ consed up the list, and QUOTE just returns its arg.
Clearly if you repeatedly INTERPRETED the above form, you get the same
result each time.  (but perhaps not within a defun which may or may
not digest the form!) Compilation makes it a bit less clear; READ is no
longer involved. What pointer are we handing it?

If you WANT the permanent change then you should presumably write
(let ((var '(a b c)))
   (defun foo (..)
     .. (nconc var value)))	
to make clear the extent of var.

> I would be unhappy if quote performed computation each time you used 
> it!  A very common idiom is to pass a quoted, uninterned symbol around
> as a marker in some  structure.  (I used to  use '(())).  This is  the
> only way  I  can  get  a  token  I  guarantee won't appear in my input
> stream!
>

So would I!!!!  I dont quite follow your example, but I'm certainly  not
suggesting that quote should creates a new list -- nor even that the  (a
b c) should be consed anew each time.  Indeed, I expect the quote should
`disappear' upon compilation. I  just wanted to  say that the  EFFECT of
that approach MAY be the most appropriate behavior.

> Anyway, the capability to side-effect constants in storage was not
> removed for taste reasons, but efficiency; it permits me as a lisp
> implementor to place code into read-only storage (and possibly share
> it among processes).

Ah, if it's in read-only storage then you can NOT modify it, no?

BTW; Tim Moore, in another posting pointed out that this is considered
`illegal' although he didn't say if CLtL specified whether an error is
signalled or whether it is simply `undefined'.

bruce

moore%cdr.utah.edu@cs.utah.edu (Tim Moore) (09/25/90)

In article <2863191137@ARTEMIS.cam.nist.gov> miller@cam.nist.gov (Bruce R. Miller) writes:
>
>In article <GUMBY.90Sep22150717@Cygnus.COM>, David Vinayak Wallace writes: 
>>    Date: 21 Sep 90 22:09:56 GMT
>>    From: miller@GEM.cam.nist.gov (Bruce R. Miller)
>>     Larry Masinter wrote:
>>    >   (let ((var '(a b c)))
>>    >       ...
>>    >       (nconc var value))
>> ...
>> Quote returns the object it was handed.  It doesn't try to guess "what
>> you meant."  After all, the code isn't text; quote just has a pointer
>> to the car of the list you handed it!
>> 
>I'm a little confused about what you're saying. In the simplest case,
>READ consed up the list, and QUOTE just returns its arg.
>Clearly if you repeatedly INTERPRETED the above form, you get the same
>result each time. (but perhaps not within a defun which may or may
>not digest the form!)

Regardless of any processing done by defun, the form is read only
once. It is true that the cons cell that is the value of var is
the same from invocation to invocation. However, the intention is that
the last element of the list whose head is that cons cell will be
changed each time through the code. So the result, (say, the return
value of nconc) is different each time.

>If you WANT the permanent change then you should presumably write
>(let ((var '(a b c)))
>   (defun foo (..)
>     .. (nconc var value)))	
>to make clear the extent of var.

This implies that foo is a closure. The previous code is an idiom that
dates from Lisps without closures. You're still trying to modify a
quoted constant here. If you have closures you might as well do

(let ((var '(c b a)))
  (defun foo (...)
    (push value var)))

>> Anyway, the capability to side-effect constants in storage was not
>> removed for taste reasons, but efficiency; it permits me as a lisp
>> implementor to place code into read-only storage (and possibly share
>> it among processes).
>
>Ah, if it's in read-only storage then you can NOT modify it, no?

If you want to modify it, don't quote it.

>
>BTW; Tim Moore, in another posting pointed out that this is considered
>`illegal' although he didn't say if CLtL specified whether an error is
>signalled or whether it is simply `undefined'.

In the terminology of CLtL, "it is an error". This means that valid
Common Lisp programs can't do it, but if it's done the results are
undefined and an error need not be signalled.

>
>bruce
Tim Moore                    moore@cs.utah.edu {bellcore,hplabs}!utah-cs!moore
"Ah, youth. Ah, statute of limitations."
		-John Waters

jeff@aiai.ed.ac.uk (Jeff Dalton) (09/26/90)

I couldn't quite make out who wrote the stuff quoted below, so I
decided to avoid the risk of misattribution by not attributing it
to anyone.

>> I would be unhappy if quote performed computation each time you used 
>> it!  A very common idiom is to pass a quoted, uninterned symbol around
>> as a marker in some  structure.  (I used to  use '(())).  This is  the
>> only way  I  can  get  a  token  I  guarantee won't appear in my input
>> stream!

I always call LIST or CONS to get a unique object, rather than rely on
anything that involves QUOTE, although some people have pointed out
that, if you want an object that cannot appear in a stream, the stream
itself will do (unless, thanks to #., the stream is held in a global
variable).  That is, you do something like:

   (with-open-stream (s ...)
     (do ((input (read s nil s) (read s nil s)))
         ((eq input s))
       (process input)))

-- Jeff

masinter@parc.xerox.com (Larry Masinter) (10/04/90)

Many quotes ago I wrote something about:

>>    >   (let ((var '(a b c)))
>>    >       ...
>>    >       (nconc var value))
>> ...

This certainly isn't legal Common Lisp, and I didn't mean to imply
that it is.  What I was trying to say was just that every Lisp
programmer will eventually stumble on a *bug* of this form and be
baffled by it.

If you wanted to do something like this actually you'd write

(defvar *values* (list 'a 'b 'c))

and then nconc *values* away. 

Somebody else wrote:

>If you WANT the permanent change then you should presumably write
>(let ((var '(a b c)))
>   (defun foo (..)
>     .. (nconc var value)))	
>to make clear the extent of var.

This is also illegal. I think in X3J13 we struggled a lot with the
wording to try to rule out the 'obvious' illegal situations. It was
hard.

Gumby (I think) wrote:

>> Anyway, the capability to side-effect constants in storage was not
>> removed for taste reasons, but efficiency; it permits me as a lisp
>> implementor to place code into read-only storage (and possibly share
>> it among processes).

I personally didn't think the issue was either taste or efficiency.
In general, I never saw that X3J13 declaring something "is an error"
in Common Lisp as an act of "removing" a capability, but rather of
just documenting a way in which program behavior was bound to be
unpredictable. I think the main problem is that you cannot even talk
about the semantics of code that modifies itself or its lexical
constants using the language in which Common Lisp is described. 

It would be unreasonable to require implementations to *signal* an
error when constants were modified because it would *require* all
implementations to store such constants in read-only storage and trap
attempts to modify them.

In some posting, Gumby said:

>I would be unhappy if quote performed computation each time you used
>it!  A very common idiom is to pass a quoted, uninterned symbol around
>as a marker in some structure.  (I used to use '(())).  This is the
>only way I can get a token I guarantee won't appear in my input
>stream!

You have to be very careful here. I remember the debate, but not the
resolution, but I believe, is that it is possible that a compiler (or
interpreter or whatever makes your Common Lisp run) may also collapse
EQUAL, quoted structures, e.g.,

  (let ((x '(())) (y '(())))
     (eq x y))

might be true, and, furthermore, the '(()) might even be collapsed
with some other unrelated lexical occurrence of a quoted structure.
(E.g., one used by READ). Thus, if you want a token that will not
occur in your input stream, you should probably do:

   (defconst *end-of-stream* (list nil))

and then use *end-of-stream* wherever you want a marker.

I think this is better style, in any case, and hardly less efficient.


     













--
Larry Masinter (masinter@parc.xerox.com)
Xerox Palo Alto Research Center (PARC)
3333 Coyote Hill Road; Palo Alto, CA USA 94304
Fax: (415) 494-4333