Reputation: 2681
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
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
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