From: Michael P. Smith
Subject: NCONC & Functions
Date: 
Message-ID: <464@ntcsd1.UUCP>
On both a TI and a Sun (using Sun Common Lisp (lucid)), the following occurs:

	>(DEFUN foo1 () '(a b c))
	>FOO1
	>(foo1)
	>(A B C)
	>(NCONC (foo1) '(d e f))
	>(A B C D E F)
	>(foo1)
	>(A B C D E F)

(DEFUN foo2 () (LIST 'a 'b 'c)) is unaffected in the same situation.

I think I understand what is happening.  NCONC is treating the pointer re-
turned by evaluating (FOO1) just as if it were evaluating a variable FOO1.
But I don't like it.  There is no variable FOO1, and NCONC is in effect re-
defining the function FOO1.

I have been using the rule: don't use destructive operations on data struc-
tures you care about.  Now this turns out to be insufficient.  Is there a
rule regarding when destructive operations can affect the values returned
by functions they are not part of?  I'm told that even FOO2 is affected when
running Franz on a DEC station.

Thanks.

	Mike Smith 	( ···@ntcsd1 | mcnc!ntcsd1!mps )

From: Richard Shu
Subject: Re: NCONC & Functions
Date: 
Message-ID: <9893@zodiac.ADS.COM>
In article <···@ntcsd1.UUCP> ···@ntcsd1.UUCP (Michael Smith) writes:
>
>On both a TI and a Sun (using Sun Common Lisp (lucid)), the following occurs:
>
>	>(DEFUN foo1 () '(a b c))
>	>FOO1
>	>(foo1)
>	>(A B C)
>	>(NCONC (foo1) '(d e f))
>	>(A B C D E F)
>	>(foo1)
>	>(A B C D E F)
>
>(DEFUN foo2 () (LIST 'a 'b 'c)) is unaffected in the same situation.

Same thing happens on a Symbolics running Genera 7.2

>I think I understand what is happening.  NCONC is treating the pointer re-
>turned by evaluating (FOO1) just as if it were evaluating a variable FOO1.
>But I don't like it.  There is no variable FOO1, and NCONC is in effect re-
>defining the function FOO1.

I'm not an expert at Common Lisp or its implementation but I think your
explanation is a little off base.  The point is that '(a b c) is a constant
and the compiler/interpreter is deciding to treat it that way.  Thus, every
call of FOO1 returns the SAME copy of '(a b c).  This can be shown by 
evaluating (eq (FOO1) (FOO1)) and seeing that the value returned is T.
For comparison (eq (FOO2) (FOO2)) returns NIL.  All this is a little weird
since (eq '(a b c) '(a b c)) also returns NIL.  I understand your confusion
but I'll bet somebody can give the "official" explanation why Common Lisp
works this way.

>I have been using the rule: don't use destructive operations on data struc-
>tures you care about.  Now this turns out to be insufficient.  

Actually, the rule is don't use destructive operations on data structures
if there might be another pointer (direct or indirect) to the same object
and that pointer should not reflect the change that a destructive operation
would make.  

Using this rule requires real thought about the life history
of the data structure you're about to change destructively.  You have to
consider every function that had a chance to reference the data structure
since it was created (which means you have to know when it was created).
This can be a real pain if data structure's life history is not readily
apparent.  As a result, I only use destructive operations when the life
history is obvious.  Typically, I do stuff like (setf (cdr x) y) where
x is a cons cell of an alist that is not part of any other alist.

You can see why the simplified version of this rule translates to:

Don't use destructive operations at all.


>Is there a
>rule regarding when destructive operations can affect the values returned
>by functions they are not part of?

I guess there's a corollary that says: make sure you create new copies of
lists if that's what you want.  Don't use QUOTE (') to cons up data structures.
If you don't like LIST, use the BACKQUOTE macro (`).  I've been shafted by
variations of your bug where the problem was using QUOTE instead of BACKQUOTE.

Rich

(responsible-p ADS message)
NIL
(si:halt)
From: ······@think.com
Subject: Backquote vs quote (was Re: NCONC & Functions)
Date: 
Message-ID: <31793@news.Think.COM>
In article <····@zodiac.ADS.COM> ····@ads.com (Richard Shu) writes:
>I guess there's a corollary that says: make sure you create new copies of
>lists if that's what you want.  Don't use QUOTE (') to cons up data structures.
>If you don't like LIST, use the BACKQUOTE macro (`).  I've been shafted by
>variations of your bug where the problem was using QUOTE instead of BACKQUOTE.

You are assuming that backquote always creates new lists.  The definition
of backquote doesn't say what it produces.  It may do as much sharing as it
can.  In particular, if a backquoted expression contains no comma, then the
backquote is equivalent to a quote.

An implementation would be justified in translating

(defun foo (a)
  `(,a b c))

into

(defun foo (a)
  (cons a '(b c)))

Then (eq (foo) (foo)) => NIL, but (eq (cdr (foo)) (cdr (foo))) => T.  And
if you NCONC something to the end of (foo) you may change the value of
future calls.
Barry Margolin, Thinking Machines Corp.

······@think.com
{uunet,harvard}!think!barmar
From: Wayne Folta
Subject: Re: Backquote vs quote (was Re: NCONC & Functions)
Date: 
Message-ID: <20990@mimsy.umd.edu>
In article <·····@news.Think.COM> ······@think.com writes:
>You are assuming that backquote always creates new lists.  The definition
>of backquote doesn't say what it produces.  It may do as much sharing as it
>can.  In particular, if a backquoted expression contains no comma, then the
>backquote is equivalent to a quote.

Exactly!  In fact, that is what happened to me when using Apple's ACL.
I am new to LISP and so I lost several hours trying to figure out why certain
data were mysteriously changing.  (Of course, the problem was that they were
'eq'.)  [As I remember it, I created a backquoted list of two sublists, one
with comma and one without.  The sublist without was reused, the part with
was not.  It sure helped disguise the problem--not that it would have been
clear to this novice anyhow :-)]


Wayne Folta          (·····@cs.umd.edu  128.8.128.8)
From: ······@think.com
Subject: Re: NCONC & Functions
Date: 
Message-ID: <31792@news.Think.COM>
In article <···@ntcsd1.UUCP> ···@ntcsd1.UUCP (Michael Smith) writes:
>	>(DEFUN foo1 () '(a b c))
>	>FOO1
>	>(foo1)
>	>(A B C)
>	>(NCONC (foo1) '(d e f))
>	>(A B C D E F)
>	>(foo1)
>	>(A B C D E F)


Someone else already explained what is going on -- your NCONC is modifying
the actual list that is in your function.  Remember, QUOTE returns its
argument, *not* a copy of it.

>(DEFUN foo2 () (LIST 'a 'b 'c)) is unaffected in the same situation.

Since this creates a new list every time it is called, destructive
operations cannot affect it.

>I have been using the rule: don't use destructive operations on data struc-
>tures you care about.  Now this turns out to be insufficient.  

A better rule is: don't return constant data structures from functions,
unless the function is documented to return a constant (so the caller knows
not to try to modify it).  A compiler would be justified in storing
constant data structures on read-only pages, so the above NCONC *could*
result in a core dump!

>								Is there a
>rule regarding when destructive operations can affect the values returned
>by functions they are not part of?  

If you modify a constant that came from a function, you may be modifying
the function.  The wording of the rule in the ANSI CL working draft is "the
consequences of using a destructive operation on a constant are undefined."

>				     I'm told that even FOO2 is affected when
>running Franz on a DEC station.

That's a bug in Franz.  Sounds like an incorrect compiler optimization to
me -- it's translating a call to LIST all of whose arguments are quoted
constants into a single quoted constant.  Since LIST is supposed to cons a
new list each time it is called, this changes the semantics drastically.
Barry Margolin, Thinking Machines Corp.

······@think.com
{uunet,harvard}!think!barmar
From: Charles A. Cox
Subject: Re: NCONC & Functions
Date: 
Message-ID: <COX.89Nov29095754@crisp.Franz.COM>
In article <·····@news.Think.COM> ······@think.com writes:
   In article <···@ntcsd1.UUCP> ···@ntcsd1.UUCP (Michael Smith) writes:
   >	>(DEFUN foo1 () '(a b c))
   >	>FOO1
   >	>(foo1)
   >	>(A B C)
   >	>(NCONC (foo1) '(d e f))
   >	>(A B C D E F)
   >	>(foo1)
   >	>(A B C D E F)

[ . . . ]

   >(DEFUN foo2 () (LIST 'a 'b 'c)) is unaffected in the same situation.

   >				     I'm told that even FOO2 is affected when
   >running Franz on a DEC station.

   That's a bug in Franz.

There's no bug here since Allegro CL is doing the right thing (i.e.
what Mr. Smith was told is incorrect).  You are correct, though, that
had it been the case that foo2 was affected, it would have been a bug.

From the Decstation:

<cl> (DEFUN foo2 () (LIST 'a 'b 'c))

FOO2 
<cl> (NCONC (foo2) '(d e f))

(A B C D E F) 
<cl> (foo2)

(A B C) 
<cl> (proclaim '(optimize (speed 3) (safety 0)))

T 
<cl> (compile 'foo2)

FOO2 
<cl> (NCONC (foo2) '(d e f))

(A B C D E F) 
<cl> (foo2)

(A B C) 
<cl> 
--
---
Charles A. Cox, Franz Inc.        1995 University Avenue, Suite 275
Internet: ···@franz.com           Berkeley, CA  94704
uucp:     uunet!franz!cox         Phone: (415) 548-3600    FAX: (415) 548-8253
From: Paul Fuqua
Subject: Re: NCONC & Functions
Date: 
Message-ID: <100049@ti-csl.csc.ti.com>
    Date: Tuesday, November 28, 1989  9:56pm (CST)
    From: barmar at think.com
    Subject: Re: NCONC & Functions
    Newsgroups: comp.lang.lisp
    
    In article <···@ntcsd1.UUCP> ···@ntcsd1.UUCP (Michael Smith) writes:
    >	>(DEFUN foo1 () '(a b c))
    >	>(NCONC (foo1) '(d e f))
    
			       A compiler would be justified in storing
    constant data structures on read-only pages, so the above NCONC *could*
    result in a core dump!

In fact, the Explorer compiler will put such constants into read-only
areas when compiling a file (just compiling a function won't do it).
For some reason, it causes lots of user confusion, so around Release 3
we modified the message for write-into-read-only errors to add the
phrase "the code was probably trying to modify a program constant" when
appropriate.

Paul Fuqua                     ··@csc.ti.com
                               {smu,texsun,cs.utexas.edu,rice}!ti-csl!pf
Texas Instruments Computer Science Center
PO Box 655474 MS 238, Dallas, Texas 75265
From: Michael P. Smith
Subject: Re: NCONC & Functions
Date: 
Message-ID: <465@ntcsd1.UUCP>
In article <···@ntcsd1.UUCP> ···@ntcsd1.UUCP (Michael Smith) writes:
>
>On both a TI and a Sun (using Sun Common Lisp (lucid)), the following occurs:
>
>	>(DEFUN foo1 () '(a b c))
>	>FOO1
>	>(foo1)
>	>(A B C)
>	>(NCONC (foo1) '(d e f))
>	>(A B C D E F)
>	>(foo1)
>	>(A B C D E F)
>
>(DEFUN foo2 () (LIST 'a 'b 'c)) is unaffected in the same situation.
>
>by functions they are not part of?  I'm told that even FOO2 is affected when
>running Franz on a DEC station.

Now I'm told by the same source that FOO2 is NOT affected in Franz.  I 
apologize posting something I hadn't ascertained myself.

BTW, thanks to some early responses I'm a little clearer about what bothers
me about this.  If FOO1 had referred to a variable whose value was changed
as a result of NCONCing the result of calling FOO1 with something else, I
would not have been surprised.  I was surprised to find that even constants
can be modified (just not re-assigned or bound), but I can live with it.  I'm
less happy that some apparently read p. 86 of Steele (1st ed.) to imply that 
quoted expressions are just like lisp constants in this respect (i.e., modifi-
able).  But what really bothers me is this: (SYMBOL-FUNCTION foo1) evaluated 
before the NCONC yields:

	(NAMED-LAMBDA FOO1 NIL
		(BLOCK FOO1
			'(A B C)))

After the NCONC, we get:

	(NAMED-LAMBDA FOO1 NIL
		(BLOCK FOO1
			'(A B C D E F))).

These look like different functions to me.  (DEFUN foo3 () *glorp*), where
*glorp* is a lisp constant assigned '(A B C) will exhibit the same behavior.
But at least its function definition remains unchanged.


	Mike Smith 	( ···@ntcsd1 | mcnc!ntcsd1!mps )


	<The views expressed above are fictional. Any resemblance to
	 real facts, whether living or dead, is purely coincidental.>
From: ······@think.com
Subject: Re: NCONC & Functions
Date: 
Message-ID: <31819@news.Think.COM>
In article <···@ntcsd1.UUCP> ···@ntcsd1.UUCP (Michael Smith) writes:
>  But what really bothers me is this: (SYMBOL-FUNCTION foo1) evaluated 
>before the NCONC yields:
>	(NAMED-LAMBDA FOO1 NIL
>		(BLOCK FOO1
>			'(A B C)))
>After the NCONC, we get:
>	(NAMED-LAMBDA FOO1 NIL
>		(BLOCK FOO1
>			'(A B C D E F))).
>These look like different functions to me.

Just because they look different doesn't mean they *are* different.  That's
the problem with destructive operations.  Consider:

(setq *foo* (list 1 2 3))
*foo* => (1 2 3)
(nconc *foo* '(a b c))
*foo* => (1 2 3 A B C)

The two values of *FOO* look different, but they are the same Lisp object.

One of the fundamental concepts of Lisp is that programs and data are made
up of the same "stuff".  Therefore, when you NCONC a list that is in a
program, you are modifying the program itself.

By the way, Lisp is *not* unique in this regard.  In C you are not
permitted to modify a string that was created as a result of a literal
string constant in the program.  In implementations that don't put them on
a read-only page you're likely to change the copy in the text section of
the program.  Consider the following program excerpt:

    func(x)
    int x;
    {
	char *string = "abcd";

	if (x) string[0] = 'q';
	printf ("%s ", string);
    }

    main()
    {
	func(0);
	func(1);
	func(0);
    }


I believe that this will print out "abcd qbcd qbcd" in many
implementations, because the string constant is modified during the second
call.
Barry Margolin, Thinking Machines Corp.

······@think.com
{uunet,harvard}!think!barmar
From: Doug Morgan
Subject: Re: NCONC & Functions
Date: 
Message-ID: <DOUG.89Nov29163722@zodiac.ADS.COM>
When the lisp reader sees:

(DEFUN foo1 () '(a b c))

it turns the text (a b c) into an internal representation as a list of
conses pointing to symbols.  At run time the ' (actually (quote ...))
returns THAT PARTICULAR internal representation.  A later NCONC
munches that representation and you get exactly what you saw.

In the case of (DEFUN foo2 () (LIST 'a 'b 'c)), the reader creates a
different internal representation.  This one is 4 conses with the
first pointing to the symbol named LIST.  At run time, the evaluation
of this representation causes a 3 cons list pointing to a b and c to
be generated.  Since these lists are generated upon each evaluation of
(LIST ...), NCONC can't affect later ones.  Again exactly as you saw.

Any lisp that treats FOO2 like FOO1 is in error, no ifs ands or buts.

I use destructive operations pretty much at will on lists that are
created from nil and the operations are in the lexical scope of the
point of creation (I get to look at everything that could be building
pointers to such lists).  Never (well, hardly ever) trust a list
that's passed in from "outside."

doug