Haskell中的异常处理

dev*_*vel 74 haskell exception-handling

我需要帮助来理解三个Haskell函数的用法

  • 试试(Control.Exception.try :: Exception e => IO a -> IO (Either e a))
  • catch(Control.Exception.catch :: Exception e => IO a -> (e -> IO a) -> IO a)
  • handle(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 ())
Run Code Online (Sandbox Code Playgroud)

我有两个问题:

  1. 如何设置自定义错误输出?

  2. 我该怎么做才能将所有错误都设置为SomeException,所以我不必编写 :: IO (Either SomeException())

捕获/试

你能给我看一个自定义错误输出的简短例子吗?

ham*_*mar 126

我什么时候使用哪个功能?

以下是Control.Exception文档的建议:

  • 如果你想要做一些清理工作的事件将引发异常,使用finally,bracketonException.
  • 要在异常后恢复并执行其他操作,最好的选择是使用其中一个try系列.
  • ...除非你从异步异常中恢复,在这种情况下使用catchcatchJust.

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

但是,有一个重要的区别.使用catch你的处理程序时不能被异步异常中断(即从另一个线程中抛出throwTo).尝试引发异步异常将阻塞,直到您的处理程序运行完毕.

请注意,catchPrelude中有不同之处,因此您可能希望这样做import Prelude hiding (catch).

handle :: Exception e =>(e - > IO a) - > IO a - > IO a

handle只是catch反转顺序的参数.使用哪一个取决于使您的代码更具可读性的原因,或者如果您想使用部分应用程序,哪一个更适合您.它们在其他方面相同.

tryJust,catchJust和handleJust

需要注意的是try,catchhandle将捕获所有指定/推断类型的异常.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
Run Code Online (Sandbox Code Playgroud)

关于纯度的说明

请注意,此类异常处理只能在不纯的代码(即IOmonad)中进行.如果您需要处理纯代码中的错误,您应该使用MaybeEither替代(或其他一些代数数据类型)查看返回值.这通常是可取的,因为它更明确,所以你总是知道在哪里会发生什么.Monads就像Control.Monad.Error这种类型的错误处理更容易使用.


也可以看看:

  • 相当翔实,但我很惊讶你从Control.Exception文档中遗漏了经验法则.即"使用`try`,除非你从异步异常中恢复,在这种情况下使用`catch`" (8认同)

har*_*arr 5

Edward Z. Yang撰写了一篇有关Haskell中的异常处理的文章:再次介绍了8种报告Haskell中的错误的方法