[Next] [Previous] [Up] [Top] [Contents] [Index]

4.7 F-Bounded Polymorphism

4.7.4 F-Bounded Polymorphism among Multiple Types

The type 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:

abstract 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;
Both 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:

template object drawing isa 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_view isa 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 } }
Both 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.


[17] Thanks to Gail Murphy for suggesting this problem to us.
The Cecil Language: Specification and Rationale, Version 2.1 - 25 MARCH 1997
[Next] [Previous] [Up] [Top] [Contents] [Index]

Generated with Harlequin WebMaker