Tipi polimorfi e ricorsivi
Tipi polimorfi
Nella scheda sui costruttori con argomenti abbiamo definito un tipo
ForseInt
per rappresentare “numeri interi o niente”. È possibile e
conveniente rendere questo tipo polimorfo, in modo che il
costruttore Proprio
sia applicabile ad argomenti di tipo
arbitrario e non necessariamente a un argomento di tipo Int
. Il
tipo che si ottiene è già definito nella libreria standard di
Haskell e si chiama Maybe
:
Notiamo che a sinistra del simbolo =
è presente una variabile di
tipo a
che rappresenta il tipo dell’argomento del costruttore
Just
. Applicando Maybe
al tipo Int
si ottiene un tipo isomorfo
a ForseInt
, con la differenza che ora Maybe
può essere applicato
a tipi diversi a seconda delle necessità.
Così come Maybe
è diventato un tipo polimorfo, i costruttori
Nothing
e Just
sono polimorfi anch’essi:
Occorre prestare attenzione al fatto che Maybe
non è più un tipo
in senso stretto, ma piuttosto un costruttore di tipo. Possiamo
pensare a Maybe
come a una funzione che, applicata a un tipo,
restituisce un altro tipo. Questa intuizione è suggerita anche dalla
sintassi Maybe T
, che è analoga a quella usata per applicare una
funzione al suo argomento.
Un tipo può avere un numero arbitrario di parametri di tipo. Ad
esempio, il (costruttore di) tipo Either
è definito nella libreria
standard di Haskell in questo modo
e consente di rappresentare l’unione disgiunta di due tipi a
e
b
. In altre parole, i valori di tipo Either T S
hanno una di due
forme possibili:
- valori della forma
Left x
dovex
è di tipoT
, oppure - valori della forma
Right y
dovey
è di tipoS
.
Tipi ricorsivi
Il costruttore di un tipo di dato T
può avere argomenti il cui
tipo è a sua volta (costruito con) T
. Per esempio, è possibile
definire il tipo delle liste con la seguente dichiarazione, che è
isomorfa a quella predefinita nella libreria standard di Haskell.
Notiamo in particolare che il costruttore Cons
ha due argomenti,
il primo che rappresenta la testa di una lista di tipo List a
e ha tipo a
e l’altro che rappresenta la coda di una lista di
tipo List a
e ha a sua volta tipo List a
.
Esercizi
- Definire le seguenti funzioni, il cui comportamento si evince dal
loro tipo.
maybeLength :: Maybe a -> Int
maybeMap :: (a -> b) -> Maybe a -> Maybe b
maybeFilter :: (a -> Bool) -> Maybe a -> Maybe a
- Il tipo
Numero
(si veda la sezione di esercizi della traccia sui costruttori con argomenti) può essere rappresentato con il tipoEither Int Float
. Ridefinire la funzionesomma :: Either Int Float -> Either Int Float -> Either Int Float
per sommare due numeri in modo tale che il risultato sia floating-point solo se necessario. - Definire la funzione
length :: List a -> Int
per calcolare la lunghezza di una lista. - Uno stream è una sequenza infinita di elementi. Definire il
tipo polimorfo
Stream a
degli stream di elementi di tipoa
e le seguenti funzioni, il cui comportamento si evince dai loro nomi e tipi.forever :: a -> Stream a
from :: Enum a => a -> Stream a
take :: Int -> Stream a -> [a]