This is what is for; however, it’s still quite verbose. To make matters worse, a function or method that accesses the same slot several times can become clogged with calls to accessor functions and **SLOT-VALUE**
. For example, even a fairly simple method such as the following, which assesses a penalty on a bank-account
if its balance falls below a certain minimum, is cluttered with calls to balance
and **SLOT-VALUE**
:
And if you decide you want to directly access the slot value in order to avoid running auxiliary methods, it gets even more cluttered.
(defmethod assess-low-balance-penalty ((account bank-account))
(when (< (slot-value account 'balance) *minimum-balance*)
(decf (slot-value account 'balance) (* (slot-value account 'balance) .01))))
The basic form of is as follows:
Each element of slots can be either the name of a slot, which is also used as a variable name, or a two-item list where the first item is a name to use as a variable and the second is the name of the slot. The instance-form is evaluated once to produce the object whose slots will be accessed. Within the body, each occurrence of one of the variable names is translated to a call to **SLOT-VALUE**
with the object and the appropriate slot name as arguments.10 Thus, you can write assess-low-balance-penalty
like this:
(with-slots (balance) account
(when (< balance *minimum-balance*)
(decf balance (* balance .01)))))
If you had defined balance
with an :accessor
rather than just a :reader
, then you could also use **WITH-ACCESSORS**
. The form of **WITH-ACCESSORS**
is the same as **WITH-SLOTS**
except each element of the slot list is a two-item list containing a variable name and the name of an accessor function. Within the body of , a reference to one of the variables is equivalent to a call to the corresponding accessor function. If the accessor function is **SETF**
able, then so is the variable.
(with-accessors ((balance balance)) account
(when (< balance *minimum-balance*)
(decf balance (* balance .01)))))
The first balance
is the name of the variable, and the second is the name of the accessor function; they don’t have to be the same. You could, for instance, write a method to merge two accounts using two calls to **WITH-ACCESSORS**
, one for each account.