Search code examples
ooplispcommon-lispsetterclos

Getters and setters to classes in Common Lisp


I often have a class that is composed of a list of another class. For example, I'll have a vector-list class made up of vectors. To avoid writing long statements, I write a method to access the embedded class. However, this method only acts as a getter; I cannot use it to set the slot value. Is there a way to use a method to set a class slot value?

Below is a minimal example:

(defclass vector ()
  ((name :accessor vector-name
         :initarg :name)))

(defclass vector-list ()
  ((vectors :accessor vector-list-vectors
            :initarg :vectors)))

(defun make-vector-list ()
  (make-instance 'vector-list
    :vectors (list
               (make-instance 'vector :name 'v1)
               (make-instance 'vector :name 'v2))))

(defmethod access-vector-name ((vt vector-list) vector-idx)
  (vector-name (nth vector-idx (vector-list-vectors vt))))


;; returns V1
(print (access-vector-name (make-vector-list) 0))

;; Now, trying to set the same slot returns an error
;; How can I set the slot?
(setf (access-vector-name (make-vector-list) 0) 'new); --> error

Solution

  • The simplest would be to write:

    (setf (aref (access-vector-name ...) index) value)`
    

    But if you don't want to expose the fact that you have arrays/vectors, you can define a custom setf expander.

    First, only define access-vector-name as a :reader in your class. Then:

    (defun (setf access-vector-name) (newval obj index)
      (setf (aref (access-vector-name obj) index) newval))
    

    If the intent is to hide the underlying implementation, maybe access-vector-name is a bad name.