This is a bug in sbcl?
Asked Answered
P

2

5

Why happen this in sbcl? Maybe a bug?

(defclass myclass ()
  ((s1
    :initform '((a . 1) (b . 2))) 
   (s2
    :initform '((a . 1) (b . 2)))))

(defparameter ins (make-instance 'myclass))

(setf (cdr (assoc 'a (slot-value ins 's1))) 43) ;; change only slot s1

;; here my problem

(slot-value ins 's1)  ;; => ((a . 44) (b . 2)))
(slot-value ins 's2)  ;; => ((a . 44) (b . 2)))

But if change :initform to :

(defclass myclass ()
  ((s1
    :initform '((a . 1) (b . 2))) 
   (s2
    :initform '((a . 1) (b . 3)))))

The problem disappears

I test this in sbcl 1.4.3 and 1.4.11. In clisp it seems that the problem does not arise.

Pleistocene answered 29/8, 2018 at 21:6 Comment(2)
Try: (eq (slot-value ins 's1) (slot-value ins 's2)) and notice what it returns.Curkell
It's allowed space optimization of static list data.Victualage
S
13

No. You are modifying literal data, which has undefined consequences.

When you quote a list in source code, it means that the thing you want to work with is exactly the list that the reader produced from your source code—this is a literal list. Such things may be remembered by the reader so that two identical lists are not duplicated.

One way to fix this is to create the lists at runtime, using list and cons:

(defclass myclass ()
  ((s1
    :initform (list (cons a 1) (cons b 2))) 
   (s2
    :initform (list (cons a 1) (cons b 2)))))
Signboard answered 29/8, 2018 at 21:17 Comment(1)
I get it, thanks for the help!!, I started to learn CL recently and I still work to understand some thingsSkiagraph
D
9

This is not a bug. '((a . 1) (b . 2)) is a literal constant and as all constants assumed immutable. That means all occurrences of '(a . 1) that also are literal can just point to the car of the other one since it should never change

Now implementations can choose to make new structures so CLISP might do that, but you cannot rely on this. You should not mutate literal data.

If you are going to change it you need to use a deep copy, like this:

(defclass myclass ()
  ((s1
    :initform (copy-tree '((a . 1) (b . 2)))) 
   (s2
    :initform (copy-tree '((a . 1) (b . 2))))))
Devastation answered 29/8, 2018 at 21:20 Comment(3)
Another problem is that even if these two assoc lists are different, they are not newly instantiated for each new instance of myclass.Mauchi
@Mauchi True, but copy-tree still is the medicine :-)Devastation
copy-alist is lighter on the stomach, and applicable here, though not as broad an antibiotic.Mauchi

© 2022 - 2024 — McMap. All rights reserved.