Bene, prima regola della gestione degli errori in Haskell: non usare maierror
.
È semplicemente terribile in ogni modo. Esiste puramente come un atto della storia 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 nel tessuto stesso della realtà, rendendo così discutibile il risultato del tuo programma.
Ora la questione diventa Maybe
vs Either
. Maybe
è adatto a qualcosa del genere head
, che può o non può restituire un valore ma c'è solo una possibile ragione per fallire. Nothing
sta dicendo qualcosa del tipo "si è rotto e sai già perché". Alcuni direbbero che indica una funzione parziale.
La forma più efficace di gestione degli errori è Either
+ un errore ADT.
Ad esempio in uno dei miei compilatori di hobby, ho qualcosa di simile
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 livello superiore. Questo può essere un errore da qualsiasi fase della compilazione o un ImpossibleError
, che indica un bug del compilatore.
Ognuno di questi tipi di errore cerca di conservare il maggior numero di informazioni il più a lungo possibile per una stampa carina o altre analisi. Ancora più importante, non avendo una stringa posso verificare che l'esecuzione di un programma non tipizzato tramite il controllo del tipo genera effettivamente un errore di unificazione! Una volta che qualcosa è a String
, è andato per sempre e tutte le informazioni in esso contenute sono opache per il compilatore / test, quindi Either String
non è neanche eccezionale.
Alla fine ho inserito questo tipo in ExceptT
un nuovo trasformatore di monade di MTL. Questo è essenzialmente EitherT
e viene fornito con una bella serie 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 altre lingue, tranne per il fatto che la cattura di un'eccezione vive IO
. Conosco alcune persone che amano usarle per IO
applicazioni pesanti in cui tutto potrebbe potenzialmente fallire, ma così raramente che non gli piace pensarci. Se usi queste impure eccezioni o semplicemente ExceptT Error IO
è una questione di gusti. Personalmente scelgo ExceptT
perché mi piace essere ricordato della possibilità di fallimento.
In sintesi,
Maybe
- Posso fallire in un modo ovvio
Either CustomType
- Posso fallire e ti dirò cosa è successo
IO
+ eccezioni - a volte fallisco. Controlla i miei documenti per vedere cosa lancio quando lo faccio
error
- Ti odio anche tu utente
head
olast
sembrano usareerror
, quindi mi chiedevo se quello fosse effettivamente un buon modo di fare le cose e mi mancava qualcosa. Questo risponde a questa domanda. :)