To illustrate slot description, we can redefine the <my-complex>
class seen before. A definition could be:
(define-class <my-complex> (<number>) (r #:init-value 0 #:getter get-r #:setter set-r! #:init-keyword #:r) (i #:init-value 0 #:getter get-i #:setter set-i! #:init-keyword #:i))
With this definition, the r
and i
slots are set to 0 by
default, and can be initialised to other values by calling make
with the #:r
and #:i
keywords. Also the generic functions
get-r
, set-r!
, get-i
and set-i!
are
automatically defined to read and write the slots.
(define c1 (make <my-complex> #:r 1 #:i 2)) (get-r c1) ⇒ 1 (set-r! c1 12) (get-r c1) ⇒ 12 (define c2 (make <my-complex> #:r 2)) (get-r c2) ⇒ 2 (get-i c2) ⇒ 0
Accessors can both read and write a slot. So, another definition of the
<my-complex>
class, using the #:accessor
option, could be:
(define-class <my-complex> (<number>) (r #:init-value 0 #:accessor real-part #:init-keyword #:r) (i #:init-value 0 #:accessor imag-part #:init-keyword #:i))
With this definition, the r
slot can be read with:
(real-part c)
and set with:
(set! (real-part c) new-value)
Suppose now that we want to manipulate complex numbers with both rectangular and polar coordinates. One solution could be to have a definition of complex numbers which uses one particular representation and some conversion functions to pass from one representation to the other. A better solution is to use virtual slots, like this:
(define-class <my-complex> (<number>) ;; True slots use rectangular coordinates (r #:init-value 0 #:accessor real-part #:init-keyword #:r) (i #:init-value 0 #:accessor imag-part #:init-keyword #:i) ;; Virtual slots access do the conversion (m #:accessor magnitude #:init-keyword #:magn #:allocation #:virtual #:slot-ref (lambda (o) (let ((r (slot-ref o 'r)) (i (slot-ref o 'i))) (sqrt (+ (* r r) (* i i))))) #:slot-set! (lambda (o m) (let ((a (slot-ref o 'a))) (slot-set! o 'r (* m (cos a))) (slot-set! o 'i (* m (sin a)))))) (a #:accessor angle #:init-keyword #:angle #:allocation #:virtual #:slot-ref (lambda (o) (atan (slot-ref o 'i) (slot-ref o 'r))) #:slot-set! (lambda(o a) (let ((m (slot-ref o 'm))) (slot-set! o 'r (* m (cos a))) (slot-set! o 'i (* m (sin a)))))))
In this class definition, the magnitude m
and angle a
slots are virtual, and are calculated, when referenced, from the normal
(i.e. #:allocation #:instance
) slots r
and i
, by
calling the function defined in the relevant #:slot-ref
option.
Correspondingly, writing m
or a
leads to calling the
function defined in the #:slot-set!
option. Thus the
following expression
(slot-set! c 'a 3)
permits to set the angle of the c
complex number.
(define c (make <my-complex> #:r 12 #:i 20)) (real-part c) ⇒ 12 (angle c) ⇒ 1.03037682652431 (slot-set! c 'i 10) (set! (real-part c) 1) (describe c) -| #<<my-complex> 401e9b58> is an instance of class <my-complex> Slots are: r = 1 i = 10 m = 10.0498756211209 a = 1.47112767430373
Since initialization keywords have been defined for the four slots, we
can now define the standard Scheme primitives make-rectangular
and make-polar
.
(define make-rectangular (lambda (x y) (make <my-complex> #:r x #:i y))) (define make-polar (lambda (x y) (make <my-complex> #:magn x #:angle y)))