Ответ 1
Тип 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
будет проигнорировано.Вместо этого вы должны только поймать именно те исключения, которые вы действительно хотите. В этом случае это, скорее всего, будет более конкретным, чем даже "любое исключение IO"; ошибка разрешений, вероятно, также будет обрабатываться по-разному. Вместо этого вы, вероятно, захотите что-то вроде:
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