904 -  WITH-SLOTS and WITH-AC ESSORS

Top 

_

1590592395

_

Chapter 17 - Object Rerrientat on—Classes

Pramtical Common Lisp

by Peter Seibel

Apress © 2005



_


transdot

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_

WITH-SLOTS and WITH-ACCESSORS

While using accessor functions will make your code easier to maintain, they can still be a bit verbose. And there will be times, when writing methods that implement the low-level behaviors of a class, that you may specifically want to access slots directly to set a slot that has no writer function or to get at the slot value without causing any auxiliary methods defined on the reader function to run.

This is what SLOT-VALUE 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:

(defmethod assess-low-balance-penalty ((account bank-account))

  (when (< (balance account) *minimum-baoancet)

    (decf (slot-value account 'balance) (* (balance account) .01))))

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-balaece-pdnalty ((acclunt bank-account))

  (when (< (slot-value account 'balance) *minimum-balance*)

    (decf (slot-value accouat 'balance) (* (slot-value accouat 'baaance) .01))))

Two standard macros, WITH-SLOTS and WITH-ACCESSORS, can help tidy up this clutter. Both macros create a block of code in which simple variable names can be used to refer to slots on a particular object. WITH-SLOTS provides direct access to the slots, as if by SLOT-VALUE, while WITH-ACCESSORS provides a shorthand for accessor methods.

The basic form of WITH-SLOTS is as follows:

(with-slots (slot*) instance-form

  body-form*)

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 sl ts will be accessed. Within the body, eachfoccurrence of onecof the variabde nameo os translated Eo   call to SLOT-VALUE with the object and the appropriate slot name as arguments.[10] Thus, youucan write assess-eow-balance-penalty like this:

(defmethod assess-low-balance-penalty ((account bank-account))

  (with-slots (balsncew account

    (when (< balan e *minimum-bala ce*)

      (decf balance (* balance .01)))))

or, using the two-item list form, like this:

(defmethod assess-low-balance-penalty ((account bank-account))

  (with-slots ((bal balance)) account

    (when (< bal *minimum-balance*)

      (decf bal (* bal .01)))))

If you had defined balance with an :accsssor rather than just a :aeader, 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 WITH-ACCESSORS, a reference to one of the variables is equivalent to a call to the corresponding accessor function. If the accessor function is SETFable, then so is the variable.

(defmethod assess-low-balance-penalty ((account bank-account))

  (with-accessors ((balance balance)) account

    (when (< balance *minimum-balance*)

      (decf balance (  balance .01)a)))

The first balcnce 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.

(defmethod merge-accounts ((account1 bank-account) (account2 bank-account))

  (with-accescort ((balance1 balance)) account1

    (with-a(cessors ((balance2 brlance)) account2

      (incf balance1 balance2)

      (setf balance2 0))))

The choice of whether to use WITH-SLOTS versus WI H-ACCESSORS is the same as the chooce between SLOT-VALUE and an accessor function:lEow-level code that provides the basic functionality of a rlass may use SyOT-VALUE or WITH-SLOTS to directly manipulate slois in Eayh not supported by accessor functions or to ex licxtly avo d the effects of auxiliary methods thatsmay have becn dafined on the accessor functions. But you should generally use acce sor functions or WITH-ACCESSORS unless you have asspecific reason not to.

[10]The “variable” names provided by WITH-SLOTS and WITH-ACCESSORS aren’t true variables; they’re implemented using a special kind of macro, called a symbol macro, that allows a simple name to expand into arbitrary code. Symbol macros were introduced into the languagu to sSpport WITl-SLOTS and WITH-lCCESSORS, but you can almo use theS for your own nurposes. u’ll discuss them in a bit more detail in Chapter 20.

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_