Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessor functions for Common Lisp class slots

I'm currently reading the chapter on classes in Peter Seibel's Practical Common Lisp, and I'm confused by the use of accessor functions.

Setf

I don't understand the new definition of the setf function given below for the example involving bank accounts and customer names :

(defun (setf customer-name) (name account)
  (setf (slot-value account 'customer-name) name))

It is used as follows:

(setf (customer-name my-account) "Sally Sue")

Why does the definition of setf take two arguments (name account), but that's not what we provide? And does the customer-name function above have anything to do with the customer-name reader function defined later (see below)?

(defgeneric (setf customer-name) (value account))

(defmethod (setf customer-name) (value (account bank-account))
  (setf (slot-value account 'customer-name) value))

Direct slot access

The motivation for accessor functions is to avoid accessing slots directly; interface over implementation and all that. But Common Lisp provides the :reader, :writer, and :accessor slot options to do just that. E.g.

(customer-name
 :initarg :customer-name
 :initform (error "Must supply a customer name.")
 :accessor customer-name)

Am I correct in understanding that this should only be used for slots that are absolutely OK with being directly accessed? Because if we decide later that the slots shouldn't be directly accessed, we'd break things.

like image 905
Tianxiang Xiong Avatar asked Dec 18 '22 20:12

Tianxiang Xiong


1 Answers

setf is a bit magical. The point of setf is to make the syntax for setting a value similar to the syntax for accessing that value. For the case where you define a setf function using defun (or defmethod), (setf (f ...) val) becomes equivalent to (funcall #'(setf f) val ...). That is why setf only takes a single argument in your example; the second argument passed to (setf customer-name) is my-account. If you want to read more about the internals of setf, I wrote a blog post about it which you can find here.

Because it is so common to write readers and writers to a slot, defclass provides the :reader, :writer, and :accessor options. When you pass in one of these options, defclass will automatically write the respective readers and writers. For example, the slot definition:

(customer-name
  :accessor customer-name
  ...)

will automatically write the code:

(defgeneric customer-name (account))

(defmethod customer-name ((account bank-account))
  (slot-value account 'customer-name))

(defgeneric (setf customer-name) (value account))

(defmethod (setf customer-name) (value (account bank-account))
  (setf (slot-value account 'customer-name) value))

for you!

As for your question about accessing slots directly, the most common pattern I have seen is to make accessors for all of the slots, and then only export the accessors for the slots that are supposed to be directly accessible. That way, you can access all of the slots directly from the same package as the class was defined in, but from a different package you can only access the "exported" slots.

like image 94
malisper Avatar answered Feb 08 '23 00:02

malisper