For Development HEAD DRAFTSearch (procedure/syntax/module):

9.19 gauche.mop.propagate - Propagating slot access

Module: gauche.mop.propagate

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.

Class: <propagate-meta>

{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.

Class: <propagate-mixin>

{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.



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT