Si è visto in precedenza che esistono due principali forme di poilmorfismo: subtyping e generics. In un linguaggio che fornisce entrambe le forme, esse interagiscono. In particolare, in Scala tali interazioni riguardano due aspetti:
Adesso, queste interazioni verranno illustrate mediante degli esempi basati sulla gerarchia di classi IntSet
, introdotta in precedenza:
abstract class IntSet { /* ... */ }
object Empty extends IntSet { /* ... */ }
class NonEmpty(elem: Int, left: IntSet, right: IntSet)
extends IntSet { /* ... */ }
Si supponga di voler definire una funzione assertiva assertAllPos
che prenda come argomenti un IntSet
e
IntSet
stesso se tutti i suoi elementi sono positiviIntuitivamente, si potrebbe pensare di definire questa funizone con dominio e codominio IntSet
:
def assertAllPos(x: IntSet): IntSet = /* ... */
Tale scelta è corretta, e in molte situazioni potrebbe essere adeguata, ma non è del tutto precisa. Infatti, il comportamento della funzione dovrebbe essere il seguente:
$$ \texttt{assertAllPos(Empty) = Empty} \\ \texttt{assertAllPos(new NonEmpty(...))=}\begin{cases} \texttt{new NonEmpty(...)} &\text{se tutti positivi} \\ \texttt{throw new Exception} &\text{altrimenti} \end{cases} $$
In base a ciò, assertAllPos
può restituire valori di diversi sottotipi di IntSet
:
Empty
, se l’argomento è il valore Empty
NonEmpty
(o Nothing
, che è sottotipo di NonEmpty
), se l’argomento è un’istanza di NonEmpty
.In sostanza il tipo preciso del valore restituito da assertAllPos
dipende dal tipo dell’argomento. Alora, si potrebbe provare a dare un tipo più preciso alla funzione introducendo un tipo parametro:
def assertAllPos[S](x: S): S = /* ... */
così, se la funzione viene invocata con un argomento di tipo Empty
restituisce un valore di tipo Empty
, mentre con un argomento di tipo NonEmpty
restituisce un valore di tipo NonEmpty
. Adesso però il tipo della funzione è “troppo genrico”: il chiamante può istanziare il tipo parametro S
in qualunque modo, ovvero può passare come argomento un valore di tipo qualsiasi, quando invece dovrebbero essere ammssi solo IntSet
e i suoi sottotipi. La soluzione è mettere un vincolo, un type bound sul tipo parametro S
, scrivendo: