4.3 Matching Against Type Patterns
'Ti
<=
prefix, binds the Ti type parameter to the dynamic type of the corresponding actual argument. Then the system attempts to match each actual explicit type parameter and each actual argument dynamic type D against its corresponding upper bound type type[param1,...,paramN]
(where N may be zero). If the upper bound is a type variable bound elsewhere in the method's header, checking this upper bound constraint is deferred until the type variable is bound. Otherwise, the system searches the supertypes of D to locate one of the form type[ptype1,...,ptypeN]
, i.e., one with the same "head" type and the same number of parameters, if any, with the additional constraint that if any of the parami is a simple type without a 'T
<=
prefix, then ptypei = parami. After finding these matching types for each upper bound, the system binds type variables: for each parameter paramj with a 'Tj
<=
prefix binding, Tj is bound to ptypej. Then the system recursively matches each ptypej against its upper bound, which may bind additional type parameters. Finally, any formal parameter whose upper bound was a type variable is checked. If any of the matches fail, then a type error results. This matching process subsumes subtyping checks: if any of the upper bounds are types with no embedded type variable bindings, the matching process reduces to a simple subtyping check.For example, consider the following code:
If the messageabstract object
printable;signature
print(@:printable):void;abstract object
numberisa
printable;abstract object
collection[T];signature do(c@:collection['T], closure:&(T):void):void;
method
print(c@:collection['T <= printable]):void { "[ ".print; do(c, &(x:T){ x.print; " ".print; }); "]".print; }method
expand_tabs(c@:'T <= collection[char]):T { -- return a copy ofc, where tab characters have been replaced with spaces
}abstract object
list[T]isa
collection[T];concrete representation
nil[T]isa
list[T];template representation
cons[T]isa
list[T];
abstract
object
table[Key,Value]isa
collection[Value];abstract
object
indexed[T]isa
table[int,T];template
object
array[T]isa
indexed[T];template
object
stringisa
indexed[char];
print
is sent to an object of dynamic type cons[number]
, then the print
method defined on collection
will be found. Then the dynamic type cons[number]
will be matched against the pattern collection['T
<=
printable]
to bind the implicit type parameter T
. The supertype graph of cons[number]
will be searched for a type of the form collection
[something]. This search will locate the type collection[number]
, binding T
to the type number
. The system then verifies that the binding for T
is a subtype of its upper bound printable
.
If, on the other hand, the message expand_tabs
is sent to an object of dynamic type string
, the method defined for collection[char]
will be found. The dynamic type string
will be matched against the static formal argument type 'T
<=
collection[char]
. This match will succeed, since string
is declared as a subtype of collection[char]
, and the implicit type parameter T
will be bound to string
.
It is illegal for a type variable to be bound more than once in a particular scope, e.g., in a method's list of explicit type parameters and in the types of its formals. It is also illegal to use a bound type variable as a parameter of an upper bound type before that type variable has been bound by the matching process. This implies that uses of a type variable as parameters must occur at greater "depth" than the binding occurrence of that type variable.
This matching process forms the heart of the semantics of implicit type parameters, and it needs to be formalized in a clearer and less algorithmic way.
Generated with Harlequin WebMaker