Path: utzoo!attcan!utgpu!jarvis.csri.toronto.edu!cs.utexas.edu!mailrus!ames!zodiac!KRONOS.ADS.COM!rar From: rar@KRONOS.ADS.COM (Bob Riemenschneider) Newsgroups: comp.lang.lisp Subject: NCONC & Functions Message-ID: <8911292103.AA21810@kronos.ads.com> Date: 29 Nov 89 21:03:45 GMT Sender: daemon@zodiac.ADS.COM Lines: 81 => From: mps@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