gpt4 book ai didi

haskell - Haskell 异常处理的正确设计

转载 作者:行者123 更新时间:2023-12-04 01:02:45 25 4
gpt4 key购买 nike

我目前正在努力思考在 Haskell 中使用异常的正确方法。异常的工作原理很简单;我试图清楚地了解解释它们的正确方法。

基本立场是,在一个设计良好的应用程序中,异常不应该逃到顶层。任何出现的异常显然都是设计者没有预料到的——即程序错误(例如,除以零),而不是不寻常的运行时发生(例如,找不到文件)。

为此,我编写了一个简单的顶级异常处理程序,它捕获所有异常并向 stderr 打印一条消息,说“这是一个错误”(在重新抛出异常以终止程序之前)。

但是,假设用户按下 Ctrl+C。这会导致抛出异常。显然,这不是任何类型的程序错误。但是,未能预测和响应这样的用户中止可能被视为错误。所以也许程序应该捕捉到它并适本地处理它,在退出之前进行任何必要的清理。

但问题是……处理这个问题的代码将捕获异常,释放任何资源或其他任何东西,然后重新抛出异常!因此,如果异常进入顶层,那并不一定意味着它没有被处理。这只是意味着我们想快速退出。

所以,我的问题是:异常是否应该以这种方式用于流量控制?每个显式捕获 UserInterrupt 的函数是否应该使用显式流控制构造手动退出而不是重新抛出异常? (但是调用者怎么知道也退出呢?) UserInterrupt 可以到达顶层吗?但在那种情况下,同样的论点,ThreadKilled 也可以吗?

简而言之,中断处理程序是否应该为 UserInterrupt (可能还有 ThreadKilled )做一个特例?那么 HeapOverflowStackOverflow 呢?那是一个错误吗?还是“程序无法控制的情况”?

最佳答案

在存在异常的情况下清理

However, failing to anticipate and react to a user abort such as this could be considered a bug. So perhaps the program should catch this and handle it appropriately, doing any necessary cleanup before exiting.



在某种意义上你是对的——程序员应该预见到异常。但不是通过捕获他们。相反,您应该使用异常安全的函数,例如 bracket .
例如:
import Control.Exception

data Resource

acquireResource :: IO Resource
releaseResource :: Resource -> IO ()

workWithResource = bracket acquireResource releaseResource $ \resource -> ...

这样无论程序是否会被 Ctrl+C 中止,资源都会被清理。

异常应该达到顶层吗?

现在,我想谈谈你的另一个陈述:

The basic position is that, in a well-designed application, exceptions shouldn't escape to the top-level.



我认为,在设计良好的应用程序中,异常是一种非常好的中止方法。如果这有任何问题,那么你做错了什么(例如,想要在 main 结束时执行清理操作——但这​​应该在 bracket 中完成!)。

这是我在程序中经常做的事情:
  • 定义一个代表任何可能错误的数据类型——任何可能出错的东西。其中一些经常包装其他异常。
    data ProgramError
    = InputFileNotFound FilePath IOException
    | ParseError FilePath String
    | ...
  • 定义如何以用户友好的方式打印错误:
    instance Show ProgramError where
    show (InputFileNotFound path e) = printf "File '%s' could not be read: %s" path (show e)
    ...
  • 将类型声明为异常:
    instance Exception ProgramError
  • 只要我愿意,就在程序中抛出这些异常。

  • 我应该捕获异常吗?

    您预期的异常必须被捕获和包装(例如在 InputFileNotFound 中)以提供更多上下文。那些你没有预料到的异常(exception)情况呢?

    我可以看到向用户打印“这是一个错误”的一些值(value),以便他们将问题报告给您。如果你这样做,你应该预料到 UserInterrupt - 正如你所说,这不是一个错误。你应该如何对待 ThreadKilled取决于您的应用程序 - 从字面上看,您是否预期它!

    然而,这与“好的设计”是正交的,并且更多地取决于您的目标用户类型、您对他们的期望以及他们对您的程序的期望。
    响应的范围可能从仅打印异常到显示“我们非常抱歉,您想向开发人员提交报告吗?”的对话框。

    关于haskell - Haskell 异常处理的正确设计,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14538294/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com