Pedro Delfino
Pedro Delfino

Reputation: 2681

What is a didatic example of the with-slots macro in CLOS?

The Common Lisp HyperSpec covers the with-slots macro. However, the example is hard to grasp.

Is there an easier and more didactic example about it?

Upvotes: 1

Views: 468

Answers (2)

Robert
Robert

Reputation: 2812

To better understand with-slots, one should look into defclass first.

No reader or writer functions are defined by default; their generation must be explicitly requested. However, slots can always be accessed using slot-value.

It means that, unless specific request, defclass don't create any accessor for the slots:

> (defclass point ()
  (x y))

(let ((new-point (make-instance 'point)))
  (setf (point-x new-point) 1))

Error: The function (COMMON-LISP:SETF COMMON-LISP-USER::POINT-X) is undefined.

In this case, one must use the slot-value function to access or modify the slot value.

(defclass point ()
  (x y))

(let ((new-point (make-instance 'point)))
  (setf (slot-value new-point 'x) 1))

Obviously, when there are several slots to update, the code become a little cumbersome:

(defmethod translate ((point-instance point) delta-x delta-y)
  (setf (slot-value point-instance 'x)
        (+ (slot-value point-instance 'x) delta-x))
  (setf (slot-value point-instance 'y)
        (+ (slot-value point-instance 'y) delta-y)))

For that reason, the with-slots macro can make the code easier to read:

(defmethod translate ((point-instance point) delta-x delta-y)
  (with-slots (x y) point-instance
    (setf x (+ x delta-x))
    (setf y (+ y delta-y))))

Upvotes: 2

Pedro Delfino
Pedro Delfino

Reputation: 2681

Yes. This (great) tutorial of 2003 has a good one from the geometry domain.

Create a class to represent points in 3-dimensions:

(defclass point ()
    (x y z))

Create a variable to instantiate the class and a function to set the values:

(defvar my-point
  (make-instance 'point))

(defun set-point-values (point x y z)
  (setf (slot-value point 'x) x
        (slot-value point 'y) y
        (slot-value point 'z) z))

In the REPL, do:

CL-USER 17 > (set-point-values my-point 3 4 12)
12

Now, think about a function to compute the distance between points. A brute force way would be:

(defun brute-force-distance-from-origin (point)
  (let ((x (slot-value point 'x))
        (y (slot-value point 'y))
        (z (slot-value point 'z)))
    (sqrt (+ (* x x)
             (* y y)
             (* z z)))))

Using the with-slots macro:

(defun distance-from-origin (point)
  (with-slots (x y z) point (sqrt (+ (* x x)
                                     (* y y)
                                     (* z z)))))

Calling the function in the REPL works as expected:

CL-USER> (distance-from-origin my-point)
13.0

Upvotes: 0

Related Questions