Why does this function return a different value every time?
Asked Answered
S

4

21

Can someone explain the following behavior? Specifically, why does the function return a different list every time? Why isn't some-list initialized to '(0 0 0) every time the function is called?

(defun foo ()
  (let ((some-list '(0 0 0)))
    (incf (car some-list))
    some-list))

Output:

> (foo)
(1 0 0)
> (foo)
(2 0 0)
> (foo)
(3 0 0)
> (foo)
(4 0 0)

Thanks!

EDIT:

Also, what is the recommended way of implementing this function, assuming I want the function to output '(1 0 0) every time?

Subminiaturize answered 22/1, 2012 at 17:8 Comment(0)
S
28

'(0 0 0) is a literal object, which is assumed to be a constant (albeit not protected from modification). So you're effectively modifying the same object every time. To create different objects at each function call use (list 0 0 0).

So unless you know, what you're doing, you should always use literal lists (like '(0 0 0)) only as constants.

Scutari answered 22/1, 2012 at 18:24 Comment(2)
Probably it would be nice to add that also quasiquoting is not guaranteed to return fresh lists.Conoid
"unless you know, what you're doing" The behavior of modifying literal data is undefined. According to the spec, you can't actually know what you're doing (with certainty), so "you should always use literal lists (like '(0 0 0)) only as constants".Meghannmegiddo
H
12

On a side note, defining this function in the sbcl REPL you get the following warning:

  caught WARNING:
    Destructive function SB-KERNEL:%RPLACA called on constant data. 
    See also: 
      The ANSI Standard, Special Operator QUOTE 
      The ANSI Standard, Section 3.2.2.3

Which gives a good hint towards the problem at hand.

Hasidism answered 22/1, 2012 at 22:51 Comment(0)
L
6

'(0 0 0) in code is literal data. Modifying this data has undefined behavior. Common Lisp implementations may not detect it at runtime (unless data is for example placed in some read-only memory space). But it can have undesirable effects.

  • you see that this data may be (and often is) shared across various invocations of the same function

  • one of the more subtle possible errors is this: Common Lisp has been defined with various optimizations which can be done by a compiler in mind. For example a compiler is allowed to reuse data:

Example:

(let ((a '(1 2 3))
      (b '(1 2 3)))
  (list a b))

In above code snippet the compiler may detect that the literal data of a and b is EQUAL. It may then have both variables point to the same literal data. Modifying it may work, but the change is visible from a and b.

Summary: Modification of literal data is a source of several subtle bugs. Avoid it if possible. Then you need to cons new data objects. Consing in general means the allocation of fresh, new data structures at runtime.

Loesceke answered 23/1, 2012 at 10:43 Comment(0)
V
-5

Wanted to write one myself, but I found a good one online:

CommonLisp has first class functions, i.e. functions are objects which can be created at runtime, and passed as arguments to other functions. --AlainPicard These first-class functions also have their own state, so they are functors. All Lisp functions are functors; there is no separation between functions that are "just code" and "function objects". The state takes the form of captured lexical variable bindings. You don't need to use LAMBDA to capture bindings; a top-level DEFUN can do it too: (let ((private-variable 42)) (defun foo () ...))

The code in the place of ... sees private-variable in its lexical scope. There is one instance of this variable associated with the one and only function object that is globally tied to the symbol FOO; the variable is captured at the time the DEFUN expression is evaluated. This variable then acts something like a static variable in C. Or, alternately, you can think of FOO as a "singleton" object with an "instance variable". --KazKylheku

Ref http://c2.com/cgi/wiki?CommonLisp

Visigoth answered 22/1, 2012 at 17:17 Comment(3)
Can you explain how the text you quoted relates to the question? I may be missing something, but I don't see it.Helms
The text explains how functions are first-class objects in Lisp, and do actually have a "state". The declared variable was part of the "state" of the function. As the text explains, this is very similar to declaring static local variables in C. What part of the text is not related to this problem?Visigoth
The part where that's not at all what is happening. Your quote talks about "captured lexical variable bindings". However some-list is a local variable of foo, it is not a captured variable and thus not part of foo's state. On each invocation of foo, some-list will have a unique binding (that as Vsevolod explained will be pointing to the same "constant" list, which explains the OP's behavior). This is entirely different than a function modifying captured variables.Helms

© 2022 - 2024 — McMap. All rights reserved.