Mat*_*hid 12 haskell exception-handling
我目前正试图围绕在Haskell中使用异常的正确方法.例外如何运作是直截了当的; 我试图清楚地了解解释它们的正确方法.
基本的立场是,在设计良好的应用程序中,异常不应该逃到顶层.任何例外显然都是设计者没有预料到的例外 - 即程序错误(例如,除以零),而不是异常的运行时发生(例如,未找到文件).
为此,我编写了一个简单的顶级异常处理程序,它捕获所有异常并打印一条消息,stderr说"这是一个错误"(在重新抛出异常以终止程序之前).
但是,假设用户按下Ctrl + C. 这会导致抛出异常.显然,这不是任何程序错误.但是,如果没有预料到并且对此用户中止做出反应可能会被视为错误.所以也许程序应该抓住它并适当地处理它,在退出之前做任何必要的清理.
但问题是......处理此问题的代码将捕获异常,释放任何资源或其他内容,然后重新抛出异常!因此,如果异常使其达到顶级,那并不一定意味着它未被处理.这只是意味着我们想要快速退出.
所以,我的问题:是否应该以这种方式将异常用于流量控制?是否每个明确捕获的函数都UserInterrupt使用显式流控制结构来手动退出而不是重新抛出异常?(但那么来电者如何知道也退出?)是否可以UserInterrupt达到顶级?但在那种情况下,ThreadKilled同样的论点也可以吗?
简而言之,中断处理程序应该为UserInterrupt(并且可能ThreadKilled)做出特殊情况吗?怎么样?HeapOverflow或者StackOverflow?是这一个错误吗?或者是"超出计划控制范围的情况"?
但是,如果没有预料到并且对此用户中止做出反应可能会被视为错误.所以也许程序应该抓住它并适当地处理它,在退出之前做任何必要的清理.
从某种意义上说,你是对的 - 程序员应该预见到异常.但不是抓住他们.相反,您应该使用异常安全的函数,例如bracket.例如:
import Control.Exception
data Resource
acquireResource :: IO Resource
releaseResource :: Resource -> IO ()
workWithResource = bracket acquireResource releaseResource $ \resource -> ...
Run Code Online (Sandbox Code Playgroud)
这样就可以清除资源,无论程序是否会被Ctrl + C中止.
现在,我想谈谈你的另一个声明:
基本的立场是,在设计良好的应用程序中,异常不应该逃到顶层.
我认为,在设计良好的应用程序中,异常是一种非常好的中止方式.如果这有任何问题,那么你做错了什么(例如,想要在结束时执行清理操作main- 但这应该在bracket!).
这是我经常在我的程序中做的事情:
定义表示任何可能错误的数据类型 - 可能出错的任何内容.其中一些经常包装其他例外.
data ProgramError
= InputFileNotFound FilePath IOException
| ParseError FilePath String
| ...
Run Code Online (Sandbox Code Playgroud)定义如何以用户友好的方式打印错误:
instance Show ProgramError where
show (InputFileNotFound path e) = printf "File '%s' could not be read: %s" path (show e)
...
Run Code Online (Sandbox Code Playgroud)将类型声明为异常:
instance Exception ProgramError
Run Code Online (Sandbox Code Playgroud)无论什么时候我都会在程序中抛出这些异常.
您预期的例外必须被捕获和包装(例如InputFileNotFound)以给予他们更多的背景.您没有预料到的例外情况如何?
我可以看到向用户打印"这是一个错误"的一些价值,以便他们向您报告问题.如果你这样做,你应该预料到UserInterrupt- 这不是一个bug,就像你说的那样.你应该如何对待ThreadKilled取决于你的应用 - 从字面上看,你是否预期它!
然而,这与"优秀设计"是正交的,更多地取决于您的目标用户类型,您对他们的期望以及他们对您的计划的期望.响应的范围可以从打印异常到"我们非常抱歉,您想向开发人员提交报告吗?"的对话框.