3.6 Type Checking Messages
@:
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:
Here the type checker would report an error, sincetemplate
object
bagisa
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
setisa
unordered_collectioninherits
bag;method
add(s@:set, x:int):void { if_not(includes(s, x), { resend(s, x) }) }
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:
The programmer could go further and move many of theabstract object
bag_like_object;field
elems(@:bag_like_object):list;template
object
bagisa
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
setisa
unordered_collection, bag_like_objectinherits
bag;method
add(s@set:bag_like_object, x:int):void { if_not(includes(s, x), { resend(s, x) }) }
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.
Generated with Harlequin WebMaker