OK, prima regola della gestione degli errori in Haskell: Non utilizzare mai error
.
È semplicemente terribile in ogni modo. Esiste puramente come un atto storico e il fatto che Prelude lo usi è terribile. Non usarlo.
L'unico momento immaginabile che potresti usare è quando qualcosa è così internamente terribile che qualcosa deve essere sbagliato con la trama stessa della realtà, rendendo così l'esito del tuo programma discutibile.
Ora la domanda diventa Maybe
rispetto a Either
. Maybe
è particolarmente adatto a qualcosa come head
, che può o non può restituire un valore ma c'è solo una possibile ragione per fallire. Nothing
sta dicendo qualcosa come "è rotto, e sai già perché". Alcuni direbbero che indica una funzione parziale.
La forma più affidabile di gestione degli errori è Either
+ un errore ADT.
Ad esempio in uno dei miei compilatori di hobby, ho qualcosa di simile a
data CompilerError = ParserError ParserError
| TCError TCError
...
| ImpossibleError String
data ParserError = ParserError (Int, Int) String
data TCError = CouldntUnify Ty Ty
| MissingDefinition Name
| InfiniteType Ty
...
type ErrorM m = ExceptT CompilerError m -- from MTL
Ora definisco un gruppo di tipi di errore, annidandoli in modo da avere un glorioso errore di primo livello. Questo può essere un errore da qualsiasi fase della compilazione o un ImpossibleError
, che indica un errore del compilatore.
Ognuno di questi tipi di errore cerca di conservare quante più informazioni possibili per una buona stampa o altre analisi. Ancora più importante, non avendo una stringa, posso verificare che l'esecuzione di un programma illtyped tramite il controllo di tipo genera effettivamente un errore di unificazione! Una volta che qualcosa è un String
, è sparito per sempre e qualsiasi informazione contenuta è opaca per il compilatore / test, quindi anche Either String
non è neanche eccezionale.
Finalmente impacchetto questo tipo in ExceptT
, un nuovo trasformatore monad da MTL. Questo è essenzialmente EitherT
e viene fornito con un bel gruppo di funzioni per lanciare e catturare errori in modo puro e piacevole.
Infine, vale la pena ricordare che Haskell ha i meccanismi per supportare la gestione delle eccezioni come fanno gli altri linguaggi, eccetto che il rilevamento di un'eccezione vive in IO
. So che alcune persone amano usare queste applicazioni per IO
pesanti dove tutto potrebbe potenzialmente fallire, ma così raramente che a loro non piace pensarci. Se si utilizzano queste eccezioni impure o solo ExceptT Error IO
è davvero una questione di gusti. Personalmente opto per ExceptT
perché mi piace essere ricordato la possibilità di un fallimento.
Come riepilogo,
-
Maybe
- Posso fallire in un modo ovvio
-
Either CustomType
- Posso fallire e ti dirò cosa è successo
-
IO
+ eccezioni: a volte non riesco. Controlla i miei documenti per vedere cosa lancio quando faccio
-
error
- Ti odio anche tu utente