4.7 F-Bounded Polymorphism
comparable
was self-recursive in a sense: its parameter type was used to link the types of the arguments to its operations. A more general case involves two or more mutually-recursive types. For example, consider a simplified model-view framework, where the model and the view must be able refer to each other and invoke operations on each other.[17] Moreover, instances of the model-view framework, such as a drawing model and a drawing view, must be able to invoke specific operations on each other without loss of type safety. To define such a framework, we exploit F-bounded style parameterized implementation strategies. The following code shows how the generic model-view framework can be defined:
Bothabstract
object
model['M <= model[M,V], 'V <= view[M,V]];field
views(@:model['M,'V]):set[V] := new_set[V]();method
register_view(m@:model['M,'V], view:V):void { m.views.add(view); }method
update(m@:model['M,'V]):void { m.views.do(&(v:V){ v.update(); }); }abstract
object
view['M <= model[M,V], 'V <= view[M,V]];field
model(@:view['M,'V]):M;signature
update(v@:view['M,'V]):void;
model
and view
are parameterized by the type of the model and the view. These formal parameters are bounded by seemingly recursively-defined instances of the model and view types. As discussed above, no problem results from this recursive nature, since the type variables M
and V
are first bound to their actual parameters, and then the upper bounds are checked. By parameterizing both model
and view
by each other's type, with the corresponding upper bound, the code in the parameterized model
and view
can be parameterized by the actual type of the instantiation of the framework. For example, the following code instantiates the generic model-view framework to construct a bitmap drawing model and view:
Bothtemplate
object
drawingisa
model[drawing,drawing_view];field
bitmap(@:drawing):bitmap;method
set_pixel(m@:drawing, pos:position, value:color):void { bitmap.pixel(pos) := value; m.views.do(&(v:drawing_view){ v.update_pixel(pos, value); }); }template
object
drawing_viewisa
view[drawing,drawing_view];method
update(v@:drawing_view):void { screen.plot(v.model.bitmap); }method
update_pixel(v@:drawing_view, pos:position, value:color):void { screen.plot_pixel(pos, value); }method
new_drawing_view(m@:drawing):drawing_view {concrete
object
isa
drawing_view { model := m } }
drawing
and drawing_view
add new operations that need to be called by the other type. By parameterizing model
as was done, the type of the views
field in drawing
is known statically to be set of (subtypes of) drawing_view
. This knowledge allows the set_pixel
operation in drawing
to invoke the update_pixel
operation without generating either a static type-error or requiring a dynamic "typecase" or "narrow" operation. Similarly, because of the way view
is parameterized, the model
field in its child drawing_view
will be known statically to refer to a (subtype of) drawing
, allowing the update
operation of drawing_view
to access the bitmap
field of the model in a statically type-safe manner.
Generated with Harlequin WebMaker