2012-05-16 16 views
14

Ho una serie di utenti, gruppi e una mappatura tra utenti e gruppi. Ho varie funzioni che manipolano questi set, tuttavia non si dovrebbe essere in grado di aggiungere un < utente -> mappatura di gruppo per un utente che non esiste, né rimuovere un gruppo che ha ancora gli utenti come membri, eccHaskell "eccezioni"

Così fondamentalmente voglio che queste funzioni generino "eccezioni" che devono essere esplicitamente trattate dal chiamante.

ho pensato di tornare qualcosa di simile:

data Return r e = Success r | Exception e 

E se il chiamante non riesce a pattern match contro il Exception, faranno si spera ottenere un avviso del compilatore, o per lo meno hanno un runtime evidente errore quando c'è un problema.

È questo l'approccio migliore, e c'è una soluzione preconfezionata che fa questo? Nota Ho bisogno di gettare e catturare "eccezioni" nel codice puro, non nell'IO Monade.

risposta

19

Sì, questo è un buon approccio, ed è nella libreria standard: Return r e è lo stesso di Either e r. È anche possibile scrivere codice come se si utilizzassero eccezioni in IO (cioè senza dover gestire esplicitamente gli errori ad ogni passo con la corrispondenza del modello): l'istanza per Either propaga gli errori, proprio come fa il monad Maybe (ma con il valore aggiuntivo e in caso di errore). Per esempio:

data MyError 
    = Oops String 
    | VeryBadError Int Int 

mightFail :: T -> Either MyError Int 
mightFail a = ... 

foo :: T -> T -> Int -> Either MyError Int 
foo a b c = do 
    x <- mightFail a 
    y <- mightFail b 
    if x == y 
     then throwError (VeryBadError x y) 
     else return (x + y + c) 

Se mightFail a o mightFail b rendimenti Left someError, poi foo a b c volontà, troppo; gli errori vengono automaticamente propagati. (Qui, throwError è solo un bel modo di scrivere Left, utilizzando le funzioni da Control.Monad.Error, c'è anche catchError per la cattura di queste eccezioni.)

11

Il tipo Return r e che si sta descrivendo è esattamente il tipo standard

data Either a b = Left a | Right b 

Si potrebbe voler usare il cosiddetto "error monad" (un nome più adatto è "exception monad") del pacchetto mtl. (In alternativa, c'è il ExceptionT nel pacchetto monadLib se non si desidera utilizzare mtl.) Ciò consente di eseguire la gestione degli errori nel codice puro invocando throwError e catchError. Here puoi trovare un esempio che mostra come usarlo.