From: Bob Riemenschneider
Subject: NCONC & Functions
Date: 
Message-ID: <8911292103.AA21810@kronos.ads.com>
=>   From: ···@ntcsd1.UUCP (Michael P. Smith)
=>
=>   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)

This seems to me to be just what you should have expected.  You stuck a
particular list structure in FOO1's function cell and destructively modified
that list structure.  Once you start using destructive operations, you have
to abandon some abstraction and think about what's really going on in memory.
That is, NCONC drags DEFUN down to it's own level.

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

Again, this is reasonable.  If you stick (LIST 'A 'B 'C) in the function cell,
every call CONSes up a new list.

=>   I have been using the rule: don't use destructive operations on data
=>   structures you care about. 

Well, that may be a good rule, depending on what you mean by "care about".
The rule I try to use is:  don't use destructive operations unless code
profiling shows you really need them in some function; then insert them,
think carefully about whether any other changes are required to preserve 
correctness, and test the modified system carefully just in case you made
a mistake.

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

I suppose one could argue that this is a legal optimization since
(LIST 'A 'B 'C) and '(A B C) are clearly equivalent in some sense.  But
there are lots of different senses of equivalence, especially when you
have destructive operations: see Ian Mason's _The Semantics of Destructive 
Lisp_.  (Note that I couldn't reproduce this in the Franz on my Sun3,
even compiling, even with a proclamation to crank optimization up all the way.)

One last note: a lot of Lispers think of this behavior as a feature rather
than a bug.  Consider the following program for computing Fibonacci numbers,
based on an example in Talcott and McCarthy's Lisp course notes:

	(defun fibon (n)
	  (if (or (eq n 0) (eq n 1)) 
	    1
	    (fibonaux n '(1 1))))

	(defun fibonaux (n l)
	  (if (null (cddr l))
	    (if (eq n 2)
	      (cadr (rplacd (cdr l) (list (+ (car l) (cadr l)))))
	      (fibonaux (- n 1) (rplacd (cdr l) (list (+ (car l) (cadr l))))))
	    (if (eq n 2)
	      (caddr l)
	      (fibonaux (- n 1) (cdr l)))))

This function modifies it's own definition at run-time, making itself 
"smarter" by caching newly computed Fibonacci numbers.  Mason says

	Although this example is trivial, it elegantly illustrates 
	why Lisp is *the* language for artificial intelligence.

Personally, I agree with John Allen's assessment:

	This technique is similar to the machine language tricks of
	self-modifying code and should be used with similar care.  The
	freedom to hang yourself should not be construed as an 
	invitation to do so, but it again points out the similarities
	of LISP to machine language and highlights the differences between 
	LISP and contemporary high-level languages.

When I recently taught a Lisp short course, I showed the example -- which
is why I had all this stuff handy -- and warned against doing such things.
It's nice to know you *can* even though you probably shouldn't.

							-- rar

From: Dan Hoey
Subject: Re: NCONC & Functions
Date: 
Message-ID: <373@ai.etl.army.mil>
In article <··················@kronos.ads.com> ···@KRONOS.ADS.COM (Bob Riemenschneider) writes:
>One last note: a lot of Lispers think of this behavior [modifying quoted
>structure in a function definition] as a feature rather
>than a bug.  Consider the following program ....

I used to think so, too, but the designers of Common Lisp have decided that
modifying quoted structure is an error.  The rationale is to allow two features
that they thought outweighed the utility of this technique:

1.  The compiler is free to collapse all EQUAL quoted structures into one,
saving some amount of space.  I do not know if this is implemented by any
compiler.

2.  The system is free to put quoted structure into write-protected memory, and
thus signal an error if the user accidentally modifies the structure.  I am
somewhat uncomfortable with this, since it also prevents the user from
purposefully modifying the structure, but since some lisp machine manufacturers
have implemented this feature, I don't think it's worth debating.

The sort of work-around that I believe is approved is (1) for now, use a
special variable, and (2) when enough lisps support it, go to DEFUNs with a
lexical environment, such as (borrowing Bob's code)

+    (let ((aux (list 1 1)))
	(defun fibon (n)
	  (if (or (eq n 0) (eq n 1)) 
	    1
!	    (fibonaux n aux))))

Dan
From: Jeff Dalton
Subject: Re: NCONC & Functions
Date: 
Message-ID: <1513@skye.ed.ac.uk>
In article <···@ai.etl.army.mil> ····@ai.etl.army.mil (Dan Hoey) writes:
>In article <··················@kronos.ads.com> ···@KRONOS.ADS.COM (Bob Riemenschneider) writes:
>>One last note: a lot of Lispers think of this behavior [modifying quoted
>>structure in a function definition] as a feature rather than a bug.
>
>I used to think so, too, but the designers of Common Lisp have decided that
>modifying quoted structure is an error.

It's not just Common Lisp.  There were other Lisps (eg, Franz Lisp)
in which modifying quoting constants might go wrong in compiled code.

-- Jeff