Il tipo Maybe è definito circa così come probabilmente sai:
data Maybe a = Just a | Nothing deriving (blabla)
Quelli a destra dell'uguale sono i costruttori per il tipo Maybe. E' comune che a volte i costruttori non abbiano lo stesso nome del tipo che "creano", in questo caso ha senso.
Di fatto un costruttore per Maybe a altro non è che una funzione con tipo (parametri...) -> Maybe a.
Il compilatore da quelle due indicazioni per il costruttore crea due funzioni cone questo tipo:
Just :: a -> Maybe a
Nothing :: Maybe a
A prima vista il tipo Maybe potrebbe sembrare in tutto e per tutto un tipo nullable, come dici tu. (A questo proposito, anche se off topic:
infoq.com/presentations/…, lol)
In Haskell però non si fa quasi mai un check esplicito su un dato di tipo Maybe a per vedere se sia Nothing o qualcosa, altrimenti il codice diventerebbe presto ingestibile.
Un esempio semplice: supponi di avere
somma :: Int -> Int -> Int
somma x y = x + y
Purtroppo i numeri provengono da un parser che gestisce l'opportunità di errore attraverso Maybe:
parseNumber :: String -> Maybe Int
Se volessimo sommare i due numeri dovremmo fare
let n1 = parseNumber input1
n2 = parseNumber input2
in
case n1 of
Just x -> case n2 of
Just y -> Just (somma x y)
Nothing -> Nothing
Nothing -> Nothing
Tutta questa espressione ha come tipo Maybe Int (questo è quello che si intende quando si dice che il typesystem di haskell mantiene la distinzione tra codice puro e codice con side effects: non è più possibile estrarre il tipo "Int" da "Maybe Int"
.
Tuttavia siccome è alquanto brutto, conviene definire una funzione che faccia un po' di pulizia:
applyMaybeBinary :: (Maybe a -> Maybe b -> Maybe c) -> Maybe a -> Maybe b
applyMaybeBinary fun Nothing _ = Nothing
applyMaybeBinary fun _ Nothing = Nothing
applyMaybeBinary fun (Just x) (Just y) = fun x y
Ora potremmo riscrivere l'espressione di prima come:
let n1 = parseNumber input1
n2 = parseNumber input2
in
applyMaybeBinary somma n1 n2
La funzione applyMaybeBinary ci permette di usare qualsiasi generica funzione binaria con parametri di tipo Maybe. Funzioni higher order di questo tipo sono molto comuni in haskell e questa operazione è solitamente detta di lifting, perché fai lavorare la funzione su tipi "superiori".
Fortunatamente tutto ciò è integrato nella libreria standard di haskell:
hackage.haskell.org/package/base-4.7.0.2/docs/…
hackage.haskell.org/package/base-4.7.0.2/docs/…
Un programmatore haskell riscriverebbe il codice sopra così:
import Control.Applicative
...
let n1 = parseNumber input1
n2 = parseNumber input2
in
(liftA2 (+)) n1 n2
Oppure più idiomaticamente
(+) <$> n1 <*> n2