Haskell 中的异常处理 [英] Exception handling in Haskell

查看:42
本文介绍了Haskell 中的异常处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I need help to understand the usage of the three Haskell functions

  • try (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. catch 和 handle 的区别在哪里?它们具有几乎相同的签名,只是顺序不同.

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

I will try to write down my trials and hope you can help me:

尝试

我有一个例子:

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

我有两个问题:

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

  1. How can I set a custom error output?

我能做些什么来将所有错误设置为 SomeException 这样我就不必编写 :: IO (Either SomeException())

What can i do to set all errors to SomeException so I dont must write the :: IO (Either SomeException())

抓住/尝试

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

Can you show me a short example with a custom error output?

推荐答案

我什么时候使用哪个函数?

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

When do I use which function?

Here's the recommendation from the Control.Exception documentation:

  • 如果您想在引发异常时进行一些清理,请使用 finallybracketonException.
  • 要在异常后恢复并执行其他操作,最好的选择是使用 try 系列之一.
  • ...除非您正在从异步异常中恢复,在这种情况下使用 catchcatchJust.
  • If you want to do some cleanup in the event that an exception is raised, use finally, bracket or onException.
  • To recover after an exception and do something else, the best choice is to use one of the try family.
  • ... unless you are recovering from an asynchronous exception, in which case use catch or catchJust.

try 需要一个 IO 动作来运行,并返回一个 Either.如果计算成功,结果会被包裹在一个 Right 构造函数中.(认为​​正确而不是错误).如果操作抛出了一个指定类型的异常,它会在Left 构造函数中返回.如果异常不是的适当类型,它会继续向上传播堆栈.指定 SomeException 作为类型将捕获所有异常,这可能是也可能不是一个好主意.

try takes an IO action to run, and returns an Either. If the computation succeeded, the result is given wrapped in a Right constructor. (Think right as opposed to wrong). If the action threw an exception of the specified type, it is returned in a Left constructor. If the exception was not of the appropriate type, it continues to propagate up the stack. Specifying SomeException as the type will catch all exceptions, which may or may not be a good idea.

请注意,如果您想从纯计算中捕获异常,则必须使用 evaluatetry 中强制执行评估.

Note that if you want to catch an exception from a pure computation, you will have to use evaluate to force evaluation within the 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 :: Exception e => IO a -> (e -> IO a) -> IO a

catch 类似于 try.它首先尝试运行指定的 IO 操作,但如果抛出异常,则处理程序将获得异常以获取替代答案.

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

catch is similar to try. It first tries to run the specified IO action, but if an exception is thrown the handler is given the exception to get an alternative answer.

main = catch (print $ 5 `div` 0) handler
  where
    handler :: SomeException -> IO ()
    handler ex = putStrLn $ "Caught exception: " ++ show ex

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

However, there is one important difference. When using catch your handler cannot be interrupted by an asynchroneous exception (i.e. thrown from another thread via throwTo). Attempts to raise an asynchroneous exception will block until your handler has finished running.

注意 Prelude 中有一个不同的 catch,所以你可能需要做 import Prelude hidden (catch).

Note that there is a different catch in the Prelude, so you might want to do import Prelude hiding (catch).

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

handle is simply catch with the arguments in the reversed order. Which one to use depends on what makes your code more readable, or which one fits better if you want to use partial application. They are otherwise identical.

请注意,trycatchhandle 将捕获指定/推断类型的所有异常.tryJust 和朋友允许你指定一个选择器函数,它过滤掉你特别想要处理的异常.例如,所有算术错误都属于 ArithException 类型.如果你只想捕捉DivideByZero,你可以这样做:

Note that try, catch and handle will catch all exceptions of the specified/inferred type. tryJust and friends allow you to specify a selector function which filters out which exceptions you specifically want to handle. For example, all arithmetic errors are of type ArithException. If you only want to catch DivideByZero, you can do:

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 使这种类型的错误处理更容易使用.


A note on purity

Note that this type of exception handling can only happen in impure code (i.e. the IO monad). If you need to handle errors in pure code, you should look into returning values using Maybe or Either instead (or some other algebraic datatype). This is often preferable as it's more explicit so you always know what can happen where. Monads like Control.Monad.Error makes this type of error handling easier to work with.

另见:

这篇关于Haskell 中的异常处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆