Node:Slot description, Next:Class precedence list, Previous:Instance creation and slot access, Up:Inheritance
When specifying a slot, a set of options can be given to the system. Each option is specified with a keyword. The list of authorized keywords is given below:
#:init-value
permits to supply a default value for the slot. This
default value is obtained by evaluating the form given after the
#:init-form
in the global environment, at class definition time.
#:init-thunk
permits to supply a thunk that will provide a
default value for the slot. The value is obtained by evaluating the
thunk a instance creation time.
#:init-keyword
permits to specify the keyword for initializing a
slot. The init-keyword may be provided during instance creation (i.e. in
the make
optional parameter list). Specifying such a keyword
during instance initialization will supersede the default slot
initialization possibly given with #:init-form
.
#:getter
permits to supply the name for the
slot getter. The name binding is done in the
environment of the define-class
macro.
#:setter
permits to supply the name for the
slot setter. The name binding is done in the
environment of the define-class
macro.
#:accessor
permits to supply the name for the
slot accessor. The name binding is done in the global
environment. An accessor permits to get and
set the value of a slot. Setting the value of a slot is done with the extended
version of set!
.
#:allocation
permits to specify how storage for
the slot is allocated. Three kinds of allocation are provided.
They are described below:
#:instance
indicates that each instance gets its own storage for
the slot. This is the default.
#:class
indicates that there is one storage location used by all
the direct and indirect instances of the class. This permits to define a
kind of global variable which can be accessed only by (in)direct
instances of the class which defines this slot.
#:each-subclass
indicates that there is one storage location used
by all the direct instances of the class. In other words, if two classes
are not siblings in the class hierarchy, they will not see the same
value.
#:virtual
indicates that no storage will be allocated for this
slot. It is up to the user to define a getter and a setter function for
this slot. Those functions must be defined with the #:slot-ref
and #:slot-set!
options. See the example below.
To illustrate slot description, we shall redefine the <complex>
class
seen before. A definition could be:
(define-class <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
slot are set to 0 by
default. Value of a slot can also be specified by calling make
with the #:r
and #:i
keywords. Furthermore, the generic
functions get-r
and set-r!
(resp. get-i
and
set-i!
) are automatically defined by the system to read and write
the r
(resp. i
) slot.
(define c1 (make <complex> #:r 1 #:i 2)) (get-r c1) => 1 (set-r! c1 12) (get-r c1) => 12 (define c2 (make <complex> #:r 2)) (get-r c2) => 2 (get-i c2) => 0
Accessors provide an uniform access for reading and writing an object
slot. Writing a slot is done with an extended form of set!
which is close to the Common Lisp setf
macro. So, another
definition of the previous <complex>
class, using the
#:accessor
option, could be:
(define-class <complex> (<number>) (r #:init-value 0 #:accessor real-part #:init-keyword #:r) (i #:init-value 0 #:accessor imag-part #:init-keyword #:i))
Using this class definition, reading the real part of the c
complex can be done with:
(real-part c)and setting it to the value contained in the
new-value
variable
can be done using the extended form of set!
.
(set! (real-part c) new-value)
Suppose now that we have to manipulate complex numbers with rectangular
coordinates as well as with 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 uses virtual slots. A
complete definition of the <complex>
class using virtual slots is
given in Figure 2.
(define-class <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)))))))Fig 2: A<complex>
number class definition using virtual slots
This class definition implements two real slots (r
and
i
). Values of the m
and a
virtual slots are
calculated from real slot values. Reading a virtual slot leads to the
application of the function defined in the #:slot-ref
option. Writing such a slot leads to the application of the function
defined in the #:slot-set!
option. For instance, the following
expression
(slot-set! c 'a 3)
permits to set the angle of the c
complex number. This expression
conducts, in fact, to the evaluation of the following expression
((lambda o m) (let ((m (slot-ref o 'm))) (slot-set! o 'r (* m (cos a))) (slot-set! o 'i (* m (sin a)))) c 3)
A more complete example is given below:
(define c (make <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) => #<<complex> 401e9b58> is an instance of class <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 make-rectangular
and make-polar
standard
Scheme primitives.
(define make-rectangular (lambda (x y) (make <complex> #:r x #:i y))) (define make-polar (lambda (x y) (make <complex> #:magn x #:angle y)))