gauche.mop.propagate
- Propagating slot access ¶Provides a metaclass to add :propagated
slot allocation option.
When a slot allocation has :propagated
, access
to the slot is redirected to other object’s slot.
It is handy for composite objects to keep external
interface simple, for access to the slot of inner objects
can be disguised as if it is a slot of the parent object.
An example would work better than explanation.
Suppose you have a <rect>
class to represent
generic rectangular area, and you want to use it when you create
a <viewport>
class by composition, instead of
inheritance. A simple way would be as follows:
(define-class <rect> () ((width :init-keyword :width) (height :init-keyword :height))) (define-class <viewport> () ((dimension :init-form (make <rect>)) ;; ... other slots ... ))
With this definition, whenever you want to access the viewport’s
width or height, you have to go through <rect>
object, e.g.
(~ viewport'dimension'width)
. This is not only cumbersome,
but the users of viewport class have to know that how the
viewport is composed (it’s not necessarily a bad thing,
but sometimes you may want to hide it).
Using gauche.mop.propagate
, you can define slots
width
and height
in <viewport>
class
that are proxies of <rect>
’s slots.
(use gauche.mop.propagate) (define-class <rect> () ((width :init-keyword :width) (height :init-keyword :height))) (define-class <viewport> (<propagate-mixin>) ((dimension :init-form (make <rect>)) (width :allocation :propagated :propagate 'dimension :init-keyword :width) (height :allocation :propagated :propagate 'dimension :init-keyword :height)))
With :propagated
allocation, the slots are not actually
allocated in <viewport>
instance, and accesses to the
slots are redirected to the object in the slot specified by
:propagate
slot option—in this case, the dimension
slot.
It is somewhat similar to the virtual slots, but it’s more convenient
for you don’t explicitly write procedures to redirect the access.
Now you can treat width
and height
as if they
are slots of <viewport>
. You can even make them
initialize via init-keyword
(but you can’t use
:init-form
or :init-value
; if you want to specify
default values, give the default values to the actual object).
gosh> (define vp (make <viewport> :width 640 :height 480)) vp gosh> (d vp) #<<viewport> 0xc5a1e0> is an instance of class <viewport> slots: dimension : #<<rect> 0xc5a130> width : 640 height : 480 gosh> (set! (~ vp'width) 800) #<undef> gosh> (~ vp'width) 800
Here’s two classes that enables this feature.
Usually all you have to do is to inherit <propagate-mixin>
class.
{gauche.mop.propagate
}
Adds :propagated
slot allocation.
The propagated slot has to have :propagate
slot option
which specifies the name of the slot that points to an object
that actually holds the value of the slot.
If a slot has :propagated
slot allocation but
does not have :propagate
slot option, an error is signaled.
The :propagate
slot option should have a value
of either a symbol, or a list of two symbols.
If it is a symbol, it names the slot that contains an object, whose slot with the same name of the propagate slot holds the value.
If it is a list of two symbols as (X Y)
, then
the access to this propagated slot actually works as
(slot-ref (slot-ref obj X) Y)
.
If you want to make a propagated slot initializable by init-keywords, make sure the slot holding the actual object comes before the propagated slots. Slot initialization proceeds in the order of appearance by default, and you want the actual object is created before setting values.
{gauche.mop.propagate
}
This is a convenience mixin class. Instead of giving
:metaclass <propagate-meta>
, you can just inherit
this calss to make propagated slots available.