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

3.6 Type Checking Messages

3.6.4 Type Checking Inherited Methods

Cecil does not require that a method be re-type-checked when inherited by a descendant, even if that descendant is not a subtype. This feat is accomplished by verifying that all descendant objects conform to the declared type of the corresponding formal of the inherited method. If the declared type is the type of the specializer, such as would arise with a type declaration using the @: syntax, then all descendant objects are required to be subtypes of the specializer as well. This may be constraining. For example, consider the following set and bag implementation fragments:

template object bag isa unordered_collection;
	field elems(@:bag):list;
	method add(b@:bag, x:int):void {
		b.elems := cons(b.elems, x); }
	method includes(b@:bag, x:int):bool {
		b.elems.includes(x) }
	...
template object set isa unordered_collection inherits bag;
	method add(s@:set, x:int):void {
		if_not(includes(s, x), { resend(s, x) }) 	}

Here the type checker would report an error, since set inherits from bag but is not a subtype, violating the conformance requirements for bag's elems, add, and includes methods.[12] In this case, a new type bag_like_object could be created that understood the elems and set_elems messages and the b formal of the bag add and includes methods should be changed to be of this type:

abstract object bag_like_object;
	field elems(@:bag_like_object):list;
template object bag isa unordered_collection, bag_like_object;
	method add(b@bag:bag_like_object, x:int):void {
		b.elems := cons(b.elems, x); }
	method includes(b@bag:bag_like_object, x:int):bool {
		b.elems.includes(x) }
	...
template object set isa unordered_collection, bag_like_object inherits bag;
	method add(s@set:bag_like_object, x:int):void {
		if_not(includes(s, x), { resend(s, x) }) 	}

The programmer could go further and move many of the bag operations into the bag_like_object. Eventually, set would simply inherit from bag_like_object, not bag. In this situation, all inheritance links would parallel subtyping links, and the two would not need to be distinguished.

If such reorganizations can always be made satisfactorily, with the resulting inheritance and subtyping graphs parallel, then it may not be necessary to separate inheritance from subtyping in the language. However, such an approach may not always be feasible. Creating the intermediate bag_like_object is somewhat tedious; the original code was easy to read and dynamically type-safe. Moreover, the implementation of bag might be written independently and not under control of the programmer building set. In these cases, simply reusing the implementation of bag for set is convenient. Unfortunately, Cecil's type rules currently seem to prevent the simple solution. One alternative would simply be to re-type-check a method whenever it was inherited by an object that was not also a subtype. The @: notation could be interpreted as indicating that this sort of re-type-checking was to be done. Re-type-checking would require access to at least part of the inherited method's source code, however. Another alternative would be to relax the conformance constraint for any object that inherited an overriding method. In this example, the bag add method would not need to be rewritten, since the set add method "shadows" it for the only descendant object that is not also a subtype; the includes method would still need to be rewritten. Also, the resend in the set add method would become type-incorrect, since it is passing an argument of type set to a method expecting an argument of type bag. This alternative is close to the idea of encapsulating the use of inheritance from clients, as with private inheritance in C++. We consider the separation of subtyping from inheritance, when coupled with the desire to avoid retypechecking methods when inherited, to be an important area for future work.


[12] Sets are not subtypes of bags since sets do not support the behavioral specification of bags. A client could detect the difference between a set and a bag by adding the same element twice to an unordered collection and testing how much the size of the collection changed.