Ответ 1
Когда я использую какую функцию?
Здесь приведена рекомендация из документации Control.Exception:
- Если вы хотите выполнить некоторую очистку в случае возникновения исключения, используйте
finally
,bracket
илиonException
. - Чтобы восстановить после исключения и сделать что-то еще, лучший выбор - использовать одно из семейств
try
. - ... если вы не восстанавливаете асинхронное исключение, в этом случае используйте
catch
илиcatchJust
.
try:: Exception e = > IO a → IO (Либо e a)
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
, поэтому вы можете сделать 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
вместо (или какой-либо другой алгебраический тип данных). Это часто предпочтительнее, чем более явное, поэтому вы всегда знаете, что может случиться там. Monads, как Control.Monad.Error
, облегчает работу с этим типом обработки ошибок.
См. также: