Kiedy używać której funkcji?
Oto zalecenie z dokumentacji Control.Exception:
- Jeśli chcesz zrobić porządki w przypadku, gdy jest wyjątek, stosowanie
finally
, bracket
lubonException
.
- Aby odzyskać po wyjątku i zrobić coś innego, najlepszym wyborem jest użycie jednego z plików
try
rodziny.
- ... chyba że odzyskujesz z asynchronicznego wyjątku, w takim przypadku użyj
catch
lub catchJust
.
try :: Exception e => IO a -> IO (albo ea)
try
podejmuje IO
akcję do uruchomienia i zwraca plik Either
. Jeśli obliczenia się powiodły, wynik jest zawijany w Right
konstruktorze. (Myśl dobrze, a nie źle). Jeśli akcja rzuciła wyjątek określonego typu , jest on zwracany w Left
konstruktorze. Jeśli wyjątek nie był odpowiedniego typu, propaguje dalej w górę stosu. Określenie SomeException
jako typu spowoduje przechwycenie wszystkich wyjątków, co może być dobrym pomysłem lub nie.
Zauważ, że jeśli chcesz złapać wyjątek z czystego obliczenia, będziesz musiał użyć evaluate
do wymuszenia oceny w try
.
main = do
result <- try (evaluate (5 `div` 0)) :: IO (Either SomeException Int)
case result of
Left ex -> putStrLn $ "Caught exception: " ++ show ex
Right val -> putStrLn $ "The answer was: " ++ show val
catch :: Exception e => IO a -> (e -> IO a) -> IO a
catch
jest podobny do try
. Najpierw próbuje uruchomić określoną IO
akcję, ale jeśli zostanie zgłoszony wyjątek, program obsługi otrzymuje wyjątek, aby uzyskać alternatywną odpowiedź.
main = catch (print $ 5 `div` 0) handler
where
handler :: SomeException -> IO ()
handler ex = putStrLn $ "Caught exception: " ++ show ex
Jest jednak jedna ważna różnica. Podczas korzystania catch
z programu obsługi nie można go przerwać przez wyjątek asynchroniczny (tj. Wyrzucony z innego wątku przez throwTo
). Próby wywołania asynchronicznego wyjątku będą blokowane do momentu zakończenia działania programu obsługi.
Zwróć uwagę, że catch
w Preludium jest coś innego , więc możesz chcieć to zrobić import Prelude hiding (catch)
.
handle :: Exception e => (e -> IO a) -> IO a -> IO a
handle
jest po prostu catch
z argumentami w odwrotnej kolejności. To, którego użyć, zależy od tego, co sprawia, że twój kod jest bardziej czytelny, lub który pasuje lepiej, jeśli chcesz używać częściowej aplikacji. Poza tym są identyczne.
tryJust, catchJust and handleJust
Należy zauważyć, że try
, catch
i handle
złapie wszystkie wyjątki podanego / wywnioskować typu. tryJust
i przyjaciele pozwalają ci określić funkcję selektora, która filtruje wyjątki, które chcesz obsłużyć. Na przykład wszystkie błędy arytmetyczne są typu ArithException
. Jeśli chcesz tylko złapać DivideByZero
, możesz:
main = do
result <- tryJust selectDivByZero (evaluate $ 5 `div` 0)
case result of
Left what -> putStrLn $ "Division by " ++ what
Right val -> putStrLn $ "The answer was: " ++ show val
where
selectDivByZero :: ArithException -> Maybe String
selectDivByZero DivideByZero = Just "zero"
selectDivByZero _ = Nothing
Uwaga o czystości
Zauważ, że ten typ obsługi wyjątków może mieć miejsce tylko w przypadku nieczystego kodu (np. IO
Monady). Jeśli potrzebujesz obsługiwać błędy w czystym kodzie, powinieneś spojrzeć na zwracanie wartości za pomocą Maybe
lub Either
zamiast tego (lub innego algebraicznego typu danych). Jest to często preferowane, ponieważ jest bardziej wyraźne, więc zawsze wiesz, co może się zdarzyć i gdzie. Control.Monad.Error
Takie monady ułatwiają obsługę tego typu błędów.
Zobacz też: