Exception 哈斯克尔的例外

Exception 哈斯克尔的例外,exception,haskell,Exception,Haskell,如果我理解正确的话,Haskell中的异常基本上是在IO monad中处理的。至少可以在IO monad中捕获异常 但有时,即使是纯函数也可能引发异常,例如,读取“…”::Int(当读取字符串不代表整数时)、运算符(!!)(当我们试图将项从列表范围中取出时),等等。我不否认这是真实的行为。然而,我不想仅仅为了抓住可能的异常而更改函数的签名,因为在这种情况下,我必须先通过调用堆栈更改所有函数的签名 在Haskell中是否有一些模式可以更轻松地处理IO monad之外的异常?在这种情况下,我是否应该

如果我理解正确的话,Haskell中的异常基本上是在IO monad中处理的。至少可以在IO monad中捕获异常

但有时,即使是纯函数也可能引发异常,例如,
读取“…”::Int
(当读取字符串不代表整数时)、运算符
(!!)
(当我们试图将项从列表范围中取出时),等等。我不否认这是真实的行为。然而,我不想仅仅为了抓住可能的异常而更改函数的签名,因为在这种情况下,我必须先通过调用堆栈更改所有函数的签名


在Haskell中是否有一些模式可以更轻松地处理IO monad之外的异常?在这种情况下,我是否应该使用
unsafePerformIO
?仅为捕获纯函数中的异常而使用
unsafePerformIO
有多“安全”?

在纯代码中,通常最好首先避免发生异常。也就是说,除非绝对肯定列表不是空的,否则不要使用
head
,并使用
reads
和模式匹配而不是
read
检查解析错误

我认为一个很好的经验法则是,纯代码中的异常应该只来自编程错误,即调用
error
,这些应该被视为bug,而不是异常处理程序可以处理的东西


请注意,我这里只讨论纯代码,
IO
中的异常在处理与“真实世界”交互时有时发生的异常情况时有其用途。但是,像
Maybe
error
这样的纯机制更容易使用,因此它们通常是首选的。

如果您预计像
read
这样的函数可能会导致异常,那么为什么不简单地重新构造代码以避免异常发生的可能性呢


对于你的问题,有一个更直接的答案。

这就是单子的用途!(不仅如此,异常处理也是一元习惯用法的一种用法)

您确实更改了可能失败的函数的签名(因为它们更改了语义,并且您希望根据经验在类型中反映尽可能多的语义)。但使用这些函数的代码不必在每个可故障函数上进行模式匹配;如果他们不在乎,他们可以绑定:

head :: [a] -> Maybe a

eqHead :: (Eq a) => [a] -> Maybe [a]
eqHead xs = do
    h <- head xs
    return $ filter (== h) xs
head::[a]->可能是
eqHead::(Eq a)=>[a]->可能[a]
eqHead xs=do

h我要冒一个险,强烈反对那些认为解决方案是“首先避免出现bug”的人的观点。我将围绕在这些单子中处理错误来构建代码

纯函数(和FP)的主要优点之一是能够对代码进行推理,如“如果函数的类型为
[a]->a
,那么对于类型
a
的所有列表,我将返回类型
a
”的值。类似这样的异常会从下面剪掉一条腿


head
之所以如此,一个很好的理由是,对于初学者来说,在
或者
和朋友之前学习列表操作要简单得多。但是如果你能理解一种更好的方法,我会避免这些风险。

我认为这些异常是“你有一个bug”类型的,因此不应该首先被发现。我强烈反对
IO
中的异常在Haskell中是好的和有用的。使用
可能
错误
或类似的显式方法总是会更好。推理要简单得多;异常处理代码似乎总是伴随着bug。它更容易组合。它更易于维护。他们应该被禁止。@JohnL:嗯,是的,结果有点错误。我已经修改了我的陈述。不过,我认为他们不应该被禁止。有些事情,比如异步异常(尽管它们可能很痛苦),不能用
error
之类的东西来实现。在实践中,我通常会在使用
error
时遇到我想要处理的错误,并且在使用
括号
和相关的
IO
异常函数后,我能做的最好的事情就是清理这些错误。异步异常肯定是一个问题。我应该限定我的评论,我只是指同步异常,因为我也看不到更好的异步异常解决方案。请注意,spoon确实使用了
unsafePerformIO
。它不应该也撒一些
NOINLINE
吗?或者类似的?