Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

This is a bug in sbcl?

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.

like image 265
Marcelo Muñoz Avatar asked Aug 29 '18 21:08

Marcelo Muñoz


2 Answers

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)))))
like image 99
Svante Avatar answered Nov 15 '22 08:11

Svante


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))))))
like image 40
Sylwester Avatar answered Nov 15 '22 08:11

Sylwester