I tipi polimorfici sono tipi che possono essere espressi accettando altri tipi come parametri.
Esistono sostanzialmente tre tipi di polimorfismo:
L’overloading fa uso della capacità del compiler o del run-time di distinguere dal contesto di chiamata le differenti defnizioni con lo stesso nome fornite per un’operazione.
L’esempio classico è l’operatore +
, che in moltissimi linguaggi è sovraccarico di definizioni in base ai tipi degli operandi.
Il sottotipaggio fa uso di una gerachia dei tipi per stabilire quali tipi siano “figli” di altri.
Di solito un tipo figlio è più specifico di un tipo padre e per tanto può essere usato tranquillamente in tutte quelle operazioni che si aspettano il padre. Praticamente funziona come l’ereditarietà fra gli oggetti.
Ad esempio data una funzione che trova il massimo fra due float, è possibile passare come parametro dei tipi interi poiché (secondo gli abusi gerarchici che fa il prof), il tipo intero è gerarchicamente sotto il tipo float. Così facendo però perdiamo l’informazione dei tipi di partenza (se ad esempio la funzione ritorna il numero più grande, ci ritorna un float) e siamo costretti a forzare dei casting nel caso volessimo mantenere detta informazione.
Alcune strutture dati hanno delle invarianti che ci consentono di avere type safety senza bisogno di sapere interamente la loro forma, ad esempio i set, che ci forniscono tutta una serie di operazioni (unione, intersezione, ecc.) che funzionano a prescindere dal tipo di dato che hanno gli elementi del set, per questo si dice essere un tipo parametrico.
Essenzialmente quando usiamo il polimorfismo parametrico non possiamo fare alcuna assunzione sulla forma dei parametri, quindi dobbiamo considerare ogni possibile implementazione, ragione per cui è detto anche polimorfismo universale.
In C e Java gli array sono un tipo parametrico cammuffato.
Nonostante ciò, alcuni linguaggi forniscono un operatore di type introspection che ci permette di verificare che un valore appartenga ad un tipo, rinunciando ad un po’ di astrazione.