Когда мне использовать какую функцию?
Вот рекомендация из документации Control.Exception:
- Если вы хотите сделать некоторые очистки в том случае, если возникает исключение, использовать
finally
, bracket
или onException
.
- Чтобы восстановиться после исключения и заняться чем-то другим, лучший выбор - использовать одного из членов
try
семьи.
- ... если вы не восстанавливаетесь после асинхронного исключения, в этом случае используйте
catch
или catchJust
.
try :: Exception e => IO a -> IO (Либо ea)
try
выполняет IO
действие для запуска и возвращает Either
. Если вычисление выполнено успешно, результат выдается в Right
конструкторе. (Думайте правильно, а не неправильно). Если действие вызвало исключение указанного типа , оно возвращается в Left
конструкторе. Если исключение не было подходящего типа, оно продолжает распространяться вверх по стеку. УказаниеSomeException
в качестве типа перехватит все исключения, что может быть, а может и не быть хорошей идеей.
Обратите внимание, что если вы хотите перехватить исключение из чистого вычисления, вам придется использовать evaluate
для принудительного вычисления внутри 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
похож на try
. Сначала он пытается выполнить указанное IO
действие, но если возникает исключение, обработчику предоставляется исключение для получения альтернативного ответа.
main = catch (print $ 5 `div` 0) handler
where
handler :: SomeException -> IO ()
handler ex = putStrLn $ "Caught exception: " ++ show ex
Тем не мение, есть одно важное отличие. При использовании catch
ваш обработчик не может быть прерван асинхронным исключением (то есть брошенным из другого потока через throwTo
). Попытки вызвать асинхронное исключение будут блокироваться, пока ваш обработчик не завершит работу.
Обратите внимание, что есть другой catch
в Prelude , так что вы, возможно, захотите это сделать import Prelude hiding (catch)
.
handle :: Exception e => (e -> IO a) -> IO a -> IO a
handle
просто catch
с аргументами в обратном порядке. Какой из них использовать, зависит от того, что делает ваш код более читаемым, или какой из них лучше подходит, если вы хотите использовать частичное приложение. В остальном они идентичны.
tryJust, catchJust и handleJust
Обратите внимание , что try
, catch
и handle
будет ловить все исключения из указанного / выведенного типа. tryJust
и друзья позволяют вам указать функцию выбора, которая отфильтровывает, какие исключения вы хотите обрабатывать. Например, все арифметические ошибки относятся к типу ArithException
. Если вы хотите только поймать DivideByZero
, вы можете:
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
Примечание о чистоте
Обратите внимание, что этот тип обработки исключений может происходить только в нечистом коде (т.е. IO
монаде). Если вам нужно обрабатывать ошибки в чистом коде, вам следует рассмотреть возможность возврата значений с использованием Maybe
или Either
вместо (или какого-либо другого алгебраического типа данных). Часто это предпочтительнее, поскольку он более ясен, поэтому вы всегда знаете, что и где может произойти. Монады вроде Control.Monad.Error
упрощают работу с этим типом обработки ошибок.
Смотрите также: