;;I student wrote the following code, whose behavior in clisp, below, I ;;can't explain. What can be happening here, or is this a bug in clisp? ;;> [1]> (defun prob() ;;> (let ((x '(nil))) ;;> (push 1 (elt x 0)))) ;;> PROB ;;> [2]> (prob) ;;> (1) ;;> [3]> (prob) ;;> (1 1) ;;<--------------- problem ;;> [4]> (prob) ;;> (1 1 1) ;;<--------------- problem ;;> [5]> (lisp-implementation-version ) ;;> "2.27 (released 2001-07-17) (built on tuiuiu.dcc.unicamp.br [143.106.7.90])" ;;> [6]> ;;> Bye. ;;It's the classic "mutating a constant" bug. The compiler assumes that ;;'(nil) is a constant, so it allocates storage for it at load time. ;;Hence every time 'prob' is called the 'car' of the *same* list is changed. ;;Solution: ;;(defun prob() ;; (let ((x (list nil))) ;; (push 1 (elt x 0)))) ;;This is guaranteed to create a new list every time, so 'prob' always ;;returns (1). ;;Other cases to watch out for are things like using 'nconc' on a constant ;;list: ;;(defun other-prob (y) ;; (let ((x '(nil))) ;; (setq x (nconc x y)) ;; x)) ;;(other-prob '(a b)) => (nil a b) ;;(other-prob '(oops)) => (nil a b oops) ;; ---------------------------------------------------------------------- ;;Eric Naggum: ;;* Jacques Wainer ;;| (defun prob() ;;| (let ((x '(nil))) ;;| (push 1 (elt x 0)))) ;; Where did you get the idea that you should use a quoted constant in your ;; code to begin with? I am actually seriously curious about this, because ;; it is such an odd and counterintuitive thing to want to do that I am ;; looking for some textbook author to blame or something. If you thought ;; it up on yourself, why did you think that a constant was a better choice ;; than a freshly created list? It is not the "don't modify a constant" ;; that I wonder why people do not understand, it is their initial desire to ;; use a constant in the first place that puzzles me. What core concept do ;; you have to have missed in order to believe that a constant is a suitable ;; thing to specify when you know you are going to modify some part of it? ;; A very weak conjecture on my part goes like this: People are used to ;; programs that load fresh into a pristine environment where they can wreak ;; havoc with constants and terminate wihout causing observable problems. ;; There is no corrector to the abuse of these constants and because most ;; programming languages make it easier to write constants than freshly ;; created objects, the idea that using a constant could be an error does ;; not arise consciously. So when using a constant does not work, the ;; _last_ thing they would think about is that the constant that is being ;; modified actually _survives_ between what they are certain are fresh ;; instantiations in a pristine environment. ;; If my weak conjecture has merit, the solution to this problem is not to ;; ask people to stop modifying constants, it is to teach people more about ;; the execution models of computers, operating systems, and languages. ;;---------------------------------------------------------------------- ;;> Before this event, I thought '(nil) to be operationally equivalent ;;> to (list nil), but just notationally more convenient. ;;'(nil) is alternative notation for (quote (nil)), as ' in general ;;means (quote ). Quote returns a literal object, whereas list ;;constructs a list, and you should understand the difference. ;; or: ;;Actually, '(nil) is expanded to (quote (nil)), and there is no ;;`optimization' involved. QUOTE prevents it's argument from being ;;evaluated and just returns it when called. Although possible, it ;;is rare to find a CL implementation that returns a new object ;;every time. ;;It is kind of cute hearing that there is no indication that '(nil) ;;is constant...the ' screams, "CONSTANT!!" to me just like a double ;;quoted string would in C/C++. ;; Kent M. Pitman: ;;There are, incidentally, two issues at play here: ;; - literal (self-evaluting) datum ;; - modifiable datum ;;Literal data could have been designed to inhibit evaluation but still ;;allow modification. In that case, this function would behave as if it ;;had what Algol called "own variables". That would have some practical ;;applications but would also invite still even more bugs. We finally ;;came up with LOAD-TIME-VALUE as a perspicuous way of treating data that ;;was to be unchanging at runtime but yet still modifiable. ;;---------------------------------------------------------------------- ;;Well, they are not, and in several ways: ;;CL-USER 24 > (defun foo () ;; '(nil)) ;;FOO ;;CL-USER 25 > (defun bar () ;; (list nil)) ;;BAR ;;CL-USER 26 > (eql (foo) (foo)) ;;T ;;CL-USER 27 > (eql (bar) (bar)) ;;NIL ;;FWIW, I remember that I didn't care about '(a b c) being a constant ;;either, when I made my first steps with Emacs Lisp, simply because I ;;didn't know about it. Later I read somewhere that those literals ;;actually /are/ constants, thought ``Oops, aaaaarggh'' and henceforth ;;didn't modify them anymore. Many examples in the literature happily ;;modify such constants, too, ``Object-Oriented Common Lisp'' by Stephen ;;Slade being a prominent example, I think (but the book is at home, ;;maybe my memory is wrong). ;;However, I wouldn't call that a ``bad design decision'', because in ;;many cases, a constant is exactly what you want. Consider ;;CL-USER 28 > (defun blubb (x) ;; (cons x '(1 2 3))) ;;BLUBB ;;CL-USER 29 > (compile 'blubb) ;;BLUBB ;;NIL ;;NIL ;;CL-USER 30 > (disassemble 'blubb) ;;206D4CE2: ;; 0: 3B25BC150020 cmp esp, [200015BC] ; T ;; 6: 7622 jbe L1 ;; 8: 80FD01 cmpb ch, 1 ;; 11: 751D jne L1 ;; 13: 55 push ebp ;; 14: 89E5 move ebp, esp ;; 16: FF7500 push [ebp] ;; 19: 83ED04 sub ebp, 4 ;; 22: 8B7508 move esi, [ebp+8] ;; 25: 897504 move [ebp+4], esi ;; 28: 894508 move [ebp+8], eax ;; 31: B819256F20 move eax, 206F2519 ; (1 2 3) ;; 36: C9 leave ;; 37: E9BEFF4700 jmp 20B54CCA ; # ;;L1: 42: E8B11097FF call 20045DC2 ; # ;; 47: 90 nop ;; 48: 90 nop ;; 49: 90 nop ;;NIL ;;See? Now, the (1 2 3) object is always available at address 206F2519 ;;and doesn't have to be constructed every time BLUBB is called. Would ;;you really want to put such a serious performance penalty onto every ;;user just because some newbies cannot remember not to modify ;;constants? You'd also change the semantics of the language; the CDRs ;;of the return values of different calls to BLUBB will always be EQL to ;;each other, now (being the same object). You think it would be a ;;better design if that were not so? Why? Given that you didn't know ;;about list literals being constants, I must assume that you have just ;;begun learning Lisp; but you think that you should already propose ;;serious changes to the language semantics, as you go, so to speak? ;;---------------------------------------------------------------------- ;;From: Erik Naggum ;;Newsgroups: comp.lang.lisp ;;Subject: Re: weird function behavior ;;Date: Wed, 15 May 2002 20:59:03 GMT ;;Organization: Naggum Software, Oslo, Norway ;;* Jacques Wainer ;;| no where there is any indication that '(nil) is a constant. ;; Yes, there is. It is right there in the specification. It also follows ;; from other things you ought know about Common Lisp. ;;| The fact that the complier/evaluator considers that a constant to me is a ;;| bad design decision. ;; A more irrelevant "argument" could probably not be constructed. ;;| In fact, I could accept that the compiler, with optimization set to a ;;| high level, would implement '(nil) as a constant but I would not expect ;;| the evaluator in clisp, to perform such optimization. ;; This is how it works: ;;1 The Common Lisp reader is responsible for constructing the in-memory ;; lists that make up the source program. Thus, (quote (nil)) is the ;; actual source code. ;;2 The quote special operator returns its unevaluated argument. Thus, the ;; (nil) is still the actual source code. ;;3 (elt x 0) refers to the car of one of the cons cells that make up the ;; actual source code. ;;4 Changing the car of a cons cell that the actual source code is made up of ;; _obviously_ affects the source code for the interpreter. ;;5 Common Lisp is based in the premise that the programmer knows what he is ;; doing, so if the programmer does the above, it is because he means it. ;;6 Because compiled and interpreted code in Common Lisp are required to have ;; identical semantics (meaning and behavior), the compiler has to preserve ;; this property of quoted objects, i.e., objects produced by the Common ;; Lisp reader. ;;7 This is not an optimization. An optimization would be to make two ;; _different_ '(nil) be the same. This does not happen here. ;;| Before this event, I thought '(nil) to be operationally equivalent to ;;| (list nil), but just notationally more convenient. ;; In Common Lisp, it is vitally important to understand when objects are ;; constructed and when forms are evaluated. There are four times that you ;; should be aware of: ;;A read-time. The Common Lisp reader builds the object when reading the ;; source code and the compiler arranges for the object to be re-created ;; when loading a compiled file. Obtained with the special operator quote. ;; The read macro #. also causes evaluation of expression to occur in the ;; reader and the resulting value to be returned. ;;B load-time. The Common Lisp loader builds the object either in order to ;; preserve a read-time object creation, or as obtained with the special ;; operator load-time-value. (This used to be the read macro #, -- which I ;; have a hard time understanding why was not just turned into a short-hand ;; for load-time-value.) Also controlled by eval-when :load-toplevel, or, ;; more appropriately, excluding :load-toplevel from eval-when makes it not ;; happen at load-time, because that is the default. ;;C compile-time. The file compiler normally only reads source code and ;; produces a binary file that arranges for the loader to produce the same ;; effects as it would have if it loaded the source code, and does not ;; evaluate the code it so compiles, unless eval-when :compile-toplevel is ;; used to instruct the compiler to evaluate it. Several macros do this. ;; (I mention this and eval-when because you will run into a similar kind of ;; problem sooner or later. Then you will remember this, because you are ;; annoyed by something you do not understand that bit you, now, too.) ;;D run-time. The "normal" execution time that programmers want to control. ;; At this time, it is much more important for you to understand that not ;; everything happens at run-time than exactly what happens at run-time. ;; I hope this helps. My patience with people who think their ignorance ;; gives them the right to denigrate design decisions they do not understand ;; is _extremely_ short. If you do not accept that the above is offered in ;; the best interest of helping you understand something you previously did ;; not, but come back with more negative and ignorant arroganceb about the ;; design, you will only piss people off, and I will get seriously annoyed ;; for having wasted my time on yet another unworthy person. Most of us ;; here would like to help people understand Common Lisp, but before you ;; understand, you listen. It is vital to successful learning that you do ;; not jump to conclusions and accept that you do not understand or know ;; something and do not have the right to even _have_ opinions on design ;; decisions until you know what they are. Most of the time, people who ;; know little and speak much make fools of themselves and piss people off, ;; and I have tended to express my strong desire to send these people to ;; hell on the first shuttle out of here, but I shall make an attempt to ;; delay that and ask you to think things through and try to avoid annoying ;; people by believing that you know something you do not but which is a ;; requirement for the opinions you think you have a right to have. You do ;; not have to agree with what I have told you, but you have to listen and ;; study and make a visible effort to understand before you argue against ;; it. This is how things work. In time, you will be the one giving this ;; lecture.