All Sather classes may be parametrized by one or more type parameters. Type parameters are essentially placeholders for actual types; the actual type is only known when the class is actually used. The array class, which we have already seen, is an example of a parametrized class.Whenever a parameterized type is referred to, its parameters are specified by type specifiers. The class behaves like a non-parameterized version whose body is a textual copy of the original class in which each parameter occurrence is replaced by its specified type.
As an example of a parametrized class, consider the class PAIR, which can hold two objects of arbitrary types. We will refer to the types as T1 and T2:
class PAIR{T1,T2} is readonly attr first:T1; readonly attr second:T2; create(a_first:T1, a_second:T2):SAME is res ::= new; res.first := a_first; res.second := a_second; return res; end; end; |
We can use this class to hold a pair of integers or a pair of an integer and a real etc.
c ::= #PAIR{INT,INT}(5,5); -- Holds a pair of integers d ::= #PAIR{INT,FLT}(5,5.0); -- Holds an integer and a FLT e ::= #PAIR{STR,INT}("this",5);-- A string and an integer f:INT := e.second; g:FLT := d.second; |
Thus, instead of defining a new class for each different type of pair, we can just parametrize the PAIR class with different parameters.
Parametrization is normally presented as a mechanism for achieving efficiency by specializing code to use particular types. However, parametrization plays an even more important conceptual role in a language with strong typing like Sather.
For instance, we could define a pair to hold $OBs
class OB_PAIR is readonly attr first,second:$OB; create(a_first, a_second:$OB):SAME is res ::= new; res.first := a_first; res.second := a_second; return res; end; end; -- class OB_PAIR |
There is no problem with defining OB_PAIR objects; in fact, it looks a little simpler.
c ::= #OB_PAIR(5,5); -- Holds a pair of integers d ::= #OB_PAIR(5,5.0); -- Holds an integer and a FLT |
However, when the time comes to extract the components of the pair, we are in trouble:
-- f:INT := e.second; ILLEGAL! second is declared to be a $OB |
We can typecase on the return value:
f_ob:$OB := e.second; f:INT; typecase f_ob when INT then f := f_ob; end; |
The above code has the desired effect, but is extremely cumbersome. Imagine if you had to do this every time you removed an INT from an ARRAY{INT}! Note that the above code would raise an error if the branch in the typecase does not match.
The parametrized version of the pair container gets around all these problems by essentially annotating the type of the container with the types of the objects it contains; the types of the contained objects are the type parameter.