Обработка исключений в Haskell


мне нужна помощь, чтобы понять использование трех функций Haskell

  • попробовать (Control.Exception.try :: Exception e => IO a -> IO (Either e a))
  • поймать (Control.Exception.catch :: Exception e => IO a -> (e -> IO a) -> IO a)
  • ручка (Control.Exception.handle :: Exception e => (e -> IO a) -> IO a -> IO a)

мне нужно знать несколько вещей:

  1. когда я использую функцию?
  2. как я могу использовать эту функцию с какой-нибудь простой пример?
  3. где разница между задвижкой и ручкой? Они имеют почти такую же подпись, только с другой порядок.

я постараюсь записать мои испытания и надеюсь, что вы можете мне помочь:

попробовать

у меня есть пример, как:

x = 5 `div` 0
test = try (print x) :: IO (Either SomeException ())

у меня есть два вопроса:

  1. как я могу установить пользовательский вывод ошибок?

  2. что я могу сделать, чтобы установить все ошибки в SomeException, поэтому я не должен писать :: IO (Either SomeException())

поймать/попробовать

вы можете покажите мне короткий пример с пользовательским выводом ошибок?

4 69

4 ответа:

когда я использую функцию?

вот рекомендация от управления.Исключение документации:

  • если вы хотите сделать некоторую очистку в случае возникновения исключения, используйте finally,bracket или onException.
  • чтобы восстановить после исключения и сделать что-то еще, лучший выбор-использовать один из try семья.
  • ... если вы не восстанавливаетесь из асинхронного исключения, в этом случае используйте catch или catchJust.

попробовать: исключение e => Ио а -> ИО (либо е)

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

лови :: исключение e => ИО А -> (Е -> Ио а) -> Ио в

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).

ручка :: исключение e => (е -> Ио а) -> Ио а -> и. о.

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 упрощает работу с этим типом обработки ошибок.


Читайте также:

у Эдварда З. Яна есть статья об обработке исключений в haskell: 8 способов сообщить об ошибках в Haskell revisited.

Re: Вопрос 3: поймать и обрабатывать являются то же самое (нашли через hoogle). Этот выбор обычно зависит от длины каждого аргумента. Если действие короче, используйте catch и наоборот. Простой пример дескриптора из документации:

do handle (\NonTermination -> exitWith (ExitFailure 1)) $ ...

кроме того, вы могли бы предположительно Карри функцию дескриптора, чтобы сделать пользовательский обработчик, который вы могли бы затем передать, например. (взято из документации):

let handler = handle (\NonTermination -> exitWith (ExitFailure 1))

Custom сообщения об ошибках:

do       
    let result = 5 `div` 0
    let handler = (\_ -> print "Error") :: IOException -> IO ()
    catch (print result) handler

Я вижу, что одна вещь, которая также раздражает вас (ваш второй вопрос) - это сочинение :: IO (Either SomeException ()) и это тоже раздражало меня.

Я изменил некоторый код теперь от этого:

let x = 5 `div` 0
result <- try (print x) :: IO (Either SomeException ())
case result of
    Left _ -> putStrLn "Error"
    Right () -> putStrLn "OK"

для этого:

let x = 5 `div` 0
result <- try (print x)
case result of
    Left (_ :: SomeException) -> putStrLn "Error"
    Right () -> putStrLn "OK"

для этого необходимо использовать ScopedTypeVariables расширение GHC, но я думаю, что эстетически это того стоит.