【问题标题】:Exception handling in HaskellHaskell 中的异常处理
【发布时间】:2011-05-15 15:38:21
【问题描述】:

我需要帮助来了解三个 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. catch 和 handle 的区别在哪里?它们具有几乎相同的签名,只是顺序不同。

我会试着写下我的经历,希望你能帮助我:

试试

我有一个例子:

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

我有两个问题:

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

  2. 如何将所有错误设置为 SomeException,这样我就不必编写 :: IO (Either SomeException())

捕捉/尝试

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

【问题讨论】:

    标签: haskell exception-handling


    【解决方案1】:

    什么时候使用哪个功能?

    这是来自 Control.Exception 文档的建议:

    • 如果您想在引发异常时进行一些清理,请使用finallybracketonException
    • 要在异常后恢复并执行其他操作,最好的选择是使用try 系列之一。
    • ...除非您正在从异步异常中恢复,在这种情况下使用catchcatchJust

    try :: Exception e => IO a -> IO (Either 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 :: 异常 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 从另一个线程抛出)。尝试引发异步异常将阻塞,直到您的处理程序完成运行。

    请注意,Prelude 中有一个不同的catch,因此您可能需要使用import Prelude hiding (catch)

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

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

    tryJust、catchJust 和 handleJust

    请注意,trycatchhandle 将捕获指定/推断类型的所有异常。 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 monad)。如果您需要处理纯代码中的错误,您应该使用MaybeEither(或其他一些代数数据类型)来查看返回值。这通常更可取,因为它更明确,因此您始终知道在哪里会发生什么。像 Control.Monad.Error 这样的 Monad 使这种类型的错误处理更容易使用。


    另见:

    【讨论】:

      【解决方案2】:

      我看到还有一件让你烦恼的事情(你的第二个问题)是:: 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 扩展,但我认为从美学角度来看这是值得的。

      【讨论】:

        【解决方案3】:

        Re:问题 3:catch 和 handle 是 same(通过 hoogle 找到)。使用哪个选项通常取决于每个参数的长度。如果动作较短,则使用 catch ,反之亦然。文档中的简单句柄示例:

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

        此外,您还可以通过 curry 函数来创建自定义处理程序,然后您可以传递它,例如。 (改编自文档):

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

        自定义错误消息:

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

        【讨论】:

          猜你喜欢
          • 2011-10-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-04-08
          • 2020-06-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多