§4.10 Generic callin bindings

As mentioned in §4.5.(d) replace bindings do not support subtype polymorphism in either direction. On the other hand, binding several base methods to the same callin method may require some more flexibility if these base methods have different signatures. This is where type parameter come to the rescue to allow for generic callin methods and their binding to base methods with different signatures.
Note that this rule is a generalization of rule §4.9.3.(c).

Additionally, any callin binding (before,replace,after) may declare one or more type parameters for propagating type parameters of the bound base method(s) (§4.10.(e)).

(a) Fresh type parameter

If a callin method declares a type parameter <T> for capturing a covariant return type this type T can be used for specifying the type of exactly one parameter or the return type. If a type parameter is used in more than one position of a callin method it is not considered a fresh type parameter and can thus not be bound to a covariant return type (see §4.10.(d)).

(b) Type bounds

The type parameter of a callin binding may be bounded by an upper bound as in <T extends C>. In this case T can only be instantiated by types conforming to the upper bound C.

(c) Generic replace binding

A generic callin method according to the above rules is bound using a replace binding that declares the same number of type parameters, where type parameters of the binding and its callin method are identified. If the callin method declares bounds for its type parameters so should the replace binding.

(d) Binding to a type parameter

A fresh type parameter can be used to capture arbitrary types in the base methods to be bound. The type parameter may be instantiated differently for each bound base method. By such type parameter instantiation the types in role and base signatures are actually identical, thus satisfying the requirement of two-way substitutability.

Within the body of a generic callin method no further rules have to be followed, because the fresh type variable actually guarantees, that the role method cannot replace the original value (initial argument or base-call result) with a different object, because no type exists that is guaranteed to conform to the type parameters. Yet, the type bound allows the role method to invoke methods of the provided object.

Generic replace binding
1
public team class MyTeam {
2
    protected class R playedBy Figures {
3
        callin <E extends Shape, F extends Shape> E ci(F arg) {
4
            E result= base.ci(arg);
5
            result= arg; // illegal, types E and F are incommensurable
6
            arg= result; // illegal, types E and F are incommensurable
7
            int size= arg.getSize(); // getSize() is availabel on F via type bound Shape
8
            result.resize(size);     // resize() is available on E via type bound Shape
9
            return result; // only two legal values exist: result and null
10
        }
11
        <E extends Shape, F extends Shape> 
12
        E  ci(F arg) <- replace Rectangle getBoundingBox(Shape original), 
13
                                Rectangle stretch(Square original);
14
    }
15
}
Explanation:
These declaration generate two version of the callin method ci:
  1. Rectangle ci (Shape arg)
  2. Rectangle ci (Square arg)
Within the callin method the following observations hold:
  • Line 5 is illegal for the first signature as Shape is not conform to Rectangle
  • Line 6 is illegal for the second signature as Rectangle is not conform to Square
  • Everything else is type-safe.

(e) Propagating type parameters

If a callin binding binds to a generic base method, any type parameter(s) of the base method must be propagated into the role method by declaring the callin binding with type parameters, too. By matching a type parameter of a base method with a type variable of the callin binding, this genericity is propagated through the callin binding.

1
class MyBase {
2
	<T> T getIt(T it) { return it; }
3
}
4
team class MyTeam {
5
	protected class MyRole playedBy MyBase {
6
		callin <U> U rm(U a) { return base.rm(a); }
7
		<U> U rm(U a) <- replace U getIt(U it);
8
	}
9
}
Explanation:
The callin binding declares a type parameter <U> which is used to match all occurrences of T in the signature of getIt. Thus the implementation of rm uses the type U in exactly the same generic way as getIt uses T.