In realtà è solo un normale costruttore di dati che sembra essere definito in Prelude , che è la libreria standard che viene importata automaticamente in ogni modulo.
Quello che forse è, strutturalmente
La definizione è simile a questa:
data Maybe a = Just a
| Nothing
Quella dichiarazione definisce un tipo, Maybe a
che è parametrizzato da una variabile di tipo a
, il che significa semplicemente che puoi usarlo con qualsiasi tipo al posto di a
.
Costruire e distruggere
Il tipo ha due costruttori Just a
e Nothing
. Quando un tipo ha più costruttori, significa che un valore del tipo deve essere stato costruito con uno solo dei possibili costruttori. Per questo tipo, un valore è stato costruito tramite Just
o Nothing
, non ci sono altre possibilità (non di errore).
Poiché Nothing
non ha alcun tipo di parametro, quando viene utilizzato come costruttore nomina un valore costante che è un membro del tipo Maybe a
per tutti i tipi a
. Ma il Just
costruttore ha un parametro di tipo, il che significa che quando usato come costruttore si comporta come una funzione dal tipo a
a Maybe a
, cioè ha il tipoa -> Maybe a
Quindi, i costruttori di un tipo costruiscono un valore di quel tipo; l'altro lato delle cose è quando vorresti usare quel valore, ed è qui che entra in gioco il pattern matching. A differenza delle funzioni, i costruttori possono essere utilizzati nelle espressioni di associazione di modelli e questo è il modo in cui è possibile eseguire l' analisi dei casi dei valori che appartengono a tipi con più di un costruttore.
Per utilizzare un Maybe a
valore in una corrispondenza di pattern, è necessario fornire un pattern per ogni costruttore, in questo modo:
case maybeVal of
Nothing -> "There is nothing!"
Just val -> "There is a value, and it is " ++ (show val)
In quel caso espressione, il primo modello corrisponderebbe se il valore fosse Nothing
, e il secondo corrisponderebbe se il valore fosse costruito con Just
. Se il secondo corrisponde, associa anche il nome val
al parametro che è stato passato al Just
costruttore quando è stato costruito il valore con cui stai confrontando.
Che cosa significa forse
Forse conoscevi già come funzionava; non c'è davvero alcuna magia per i Maybe
valori, è solo un normale Haskell Algebraic Data Type (ADT). Ma è usato un bel po 'perché effettivamente "solleva" o estende un tipo, come Integer
dal tuo esempio, in un nuovo contesto in cui ha un valore extra ( Nothing
) che rappresenta una mancanza di valore! Il sistema di tipi richiede quindi che tu controlli quel valore extra prima che ti permetta di arrivare a quello Integer
che potrebbe essere lì. Ciò impedisce un numero notevole di bug.
Molte lingue oggi gestiscono questo tipo di valore "senza valore" tramite riferimenti NULL. Tony Hoare, un eminente scienziato informatico (ha inventato Quicksort ed è un vincitore del Turing Award), riconosce questo come il suo "errore da un miliardo di dollari" . Il tipo Forse non è l'unico modo per risolvere questo problema, ma ha dimostrato di essere un modo efficace per farlo.
Forse come Functor
L'idea di trasformare un tipo ad un altro in modo tale che le operazioni sul vecchio tipo possono anche essere trasformati in lavoro sul nuovo tipo è il concetto alla base della classe tipo Haskell chiamato Functor
, che Maybe a
ha un esempio utile.
Functor
fornisce un metodo chiamato fmap
, che mappa le funzioni che vanno dai valori del tipo base (come Integer
) a funzioni che vanno dai valori del tipo elevato (come Maybe Integer
). Una funzione trasformata fmap
per lavorare su un Maybe
valore funziona così:
case maybeVal of
Nothing -> Nothing -- there is nothing, so just return Nothing
Just val -> Just (f val) -- there is a value, so apply the function to it
Quindi, se hai un Maybe Integer
valore m_x
e una Int -> Int
funzione f
, puoi fmap f m_x
applicare la funzione f
direttamente a Maybe Integer
senza preoccuparti se ha effettivamente un valore o meno. In effetti, potresti applicare un'intera catena di Integer -> Integer
funzioni elevate ai Maybe Integer
valori e Nothing
devi preoccuparti di controllare esplicitamente una volta sola quando hai finito.
Forse come monade
Non sono sicuro di quanto tu abbia familiarità con il concetto di Monad
ancora, ma almeno l'hai usato IO a
prima e la firma del tipo IO a
sembra notevolmente simile a Maybe a
. Sebbene IO
sia speciale in quanto non ti espone i suoi costruttori e può quindi essere "eseguito" solo dal sistema di runtime Haskell, è anche un Functor
oltre ad essere un file Monad
. In effetti, c'è un senso importante in cui a Monad
è solo un tipo speciale Functor
con alcune funzionalità extra, ma non è questo il posto per entrare in questo.
Ad ogni modo, alle monadi piacciono i IO
tipi di mappatura su nuovi tipi che rappresentano "calcoli che si traducono in valori" e puoi elevare le funzioni in Monad
tipi tramite una fmap
funzione molto simile chiamata liftM
che trasforma una funzione regolare in un "calcolo che risulta nel valore ottenuto valutando il funzione."
Probabilmente hai intuito (se hai letto fin qui) che Maybe
è anche un file Monad
. Rappresenta "calcoli che potrebbero non riuscire a restituire un valore". Proprio come fmap
nell'esempio, questo ti consente di eseguire un sacco di calcoli senza dover controllare esplicitamente gli errori dopo ogni passaggio. In effetti, il modo in cui Monad
viene costruita l' istanza, un calcolo sui Maybe
valori si interrompe non appena Nothing
si incontra a, quindi è un po 'come un aborto immediato o un ritorno senza valore nel mezzo di un calcolo.
Potresti aver scritto forse
Come ho detto prima, non c'è nulla di inerente al Maybe
tipo che è integrato nella sintassi del linguaggio o nel sistema di runtime. Se Haskell non lo ha fornito per impostazione predefinita, potresti fornire tu stesso tutte le sue funzionalità! In effetti, potresti riscriverlo comunque tu stesso, con nomi diversi, e ottenere la stessa funzionalità.
Si spera che tu capisca il Maybe
tipo e i suoi costruttori ora, ma se c'è ancora qualcosa di poco chiaro, fammelo sapere!
Maybe
dove altre lingue userebberonull
onil
(con brutti messaggi inNullPointerException
agguato in ogni angolo). Ora anche altri linguaggi iniziano a usare questo costrutto: Scala asOption
, e anche Java 8 avrà ilOptional
tipo.