Тип Control.Exception.handle
:
handle :: Exception e => (e -> IO a) -> IO a -> IO a
Проблема, которую вы видите, состоит в том, что лямбда-выражение (\_ -> return "err")
не относится к типу e -> IO a
, где e
является экземпляром Exception
. Ясно как грязь? Хорошо. Теперь я предоставлю решение, которое на самом деле должно быть полезным:)
В вашем случае просто так получается, что e
должно быть Control.Exception.ErrorCall
, поскольку undefined
использует error
, который выбрасывает ErrorCall
(экземпляр Exception
).
Для обработки использования undefined
вы можете определить что-то вроде handleError
:
handleError :: (ErrorCall -> IO a) -> IO a -> IO a
handleError = handle
По сути, это псевдоним Control.Exception.handle
с e
, зафиксированным как ErrorCall
, который error
выбрасывает.
Выглядит так при запуске в GHCi 7.4.1 :
ghci> handleError (\_ -> return "err") undefined
"err"
Для обработки всех исключений функцию handleAll
можно записать следующим образом:
handleAll :: (SomeException -> IO a) -> IO a -> IO a
handleAll = handle
Перехват всех исключений имеет последствия, хорошо описанные в этом отрывке документации Control.Exception
:
Перехват всех исключений
Можно перехватить все исключения, используя тип SomeException
:
catch f (\e -> ... (e :: SomeException) ...)
ОДНАКО, это обычно не то, что вы хотите сделать!
Например, предположим, что вы хотите прочитать файл, но если он не существует, продолжайте, как если бы он содержал ""
. Может возникнуть соблазн просто перехватить все исключения и вернуть ""
в обработчик. Однако это имеет всевозможные нежелательные последствия. Например, если пользователь нажимает control-C как раз в нужный момент, то будет зафиксировано исключение UserInterrupt
, и программа продолжит работу, полагая, что файл содержит ""
. Аналогично, если другой поток попытается уничтожить поток, читающий файл, исключение ThreadKilled
будет проигнорировано.
Вместо этого вы должны ловить только те исключения, которые вам действительно нужны. В этом случае это, вероятно, будет более конкретным, чем даже «любое исключение ввода-вывода»; ошибка разрешений, вероятно, также будет обрабатываться по-другому. Вместо этого вы, вероятно, захотите что-то вроде:
e <- tryJust (guard . isDoesNotExistError) (readFile f)
let str = either (const "") id e
Бывают случаи, когда вам действительно нужно поймать какое-либо исключение. Тем не менее, в большинстве случаев это просто так, вы можете сделать некоторые очистки; вы на самом деле не заинтересованы в самом исключении. Например, если вы открываете файл, то хотите снова его закрыть, независимо от того, выполняется ли обработка файла нормально или выдает исключение. Однако в этих случаях вы можете использовать такие функции, как bracket
, finally
и onException
, которые фактически никогда не передают вам исключение, а просто вызывают функции очистки в соответствующих точках.
Но иногда вам действительно нужно перехватить любое исключение и посмотреть, что это за исключение. Один пример находится на самом верхнем уровне программы, вы можете перехватить любое исключение, распечатать его в лог-файл или на экран, а затем завершить работу корректно. В этих случаях вы можете использовать catch
(или одну из других функций перехвата исключений) с типом SomeException
.
Источник: http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception.html#g:4