Haskell异常的含义

tel*_*one 27 haskell

Haskell中异常的含义是什么?我看到的唯一用法是放入undefined或error放入我的代码中以阻止程序运行.否则,我认为将异常编程作为逻辑设计缺陷.但是Haskell有一个高级异常模块Control.Exception,它被使用Prelude.我读到C++中异常的原因是为了防止代码中出现很多"调用函数,然后检查状态".但是这些东西可以在Haskell中抽象出来.我可以在Haskell中看到异常处理的另一个原因是使用FFI处理外部异常,但仅限于内部用于包装调用的Haskell函数.

Dan*_*ton 27

在我看来,例外意味着"你违反了职能合同".我不是在谈论类型合同,我在谈论你通常在评论中找到的东西.

-- The reciprocal is not defined for the value 0
myRecip :: Fractional a => a -> a
myRecip x | x == 0    = throw DivideByZero
          | otherwise = 1 / x
Run Code Online (Sandbox Code Playgroud)

当然,你总能以"安全"的方式提供这些功能:

safeRecip :: Fractional a => a -> Maybe a
safeRecip x | x == 0    = Nothing
            | otherwise = Just $ 1 / x
Run Code Online (Sandbox Code Playgroud)

也许我们甚至应该抽象出这种模式

restrictInput :: (a -> Bool) -> (a -> b) -> (a -> Maybe b)
restrictInput pred f x = if pred x then Just (f x) else Nothing

safeRecip' = restrictInput (/= 0) myRecip
Run Code Online (Sandbox Code Playgroud)

您可以想象使用类似的组合器来Either代替Maybe报告故障.因为我们有能力用纯函数处理这些东西,为什么还要用不纯的异常模型呢?好吧,大多数Haskellers会告诉你坚持纯洁.但是黑暗的事实是,添加额外的层只是一种痛苦.你不能再写了

prop_recip x = x == (recip . recip) x
Run Code Online (Sandbox Code Playgroud)

因为现在结果recip x是在Maybe.对于您不能再执行的(a -> a)功能,您可以执行一些有用的操作.现在你必须考虑组成Maybes.当然,如果你对monad感到满意,这是微不足道的:

prop_recip 0 = (safeRecip >=> safeRecip) 0 == Nothing
prop_recip x = (safeRecip >=> safeRecip) x == Just x
Run Code Online (Sandbox Code Playgroud)

但其中存在着摩擦.在涉及一元作文时,新手通常几乎一无所知.Haskell委员会,就像#haskell irc频道上的许多人会很快告诉你的那样,为了迎合新手而做出了一些关于语言设计的相当不稳定的决定.我们希望能够说"你不需要知道monad就可以开始在Haskell中制作有用的东西".我普遍同意这种观点.

tl; dr 这个问题的几个快速答案:什么是例外?

  • 一个肮脏的黑客,所以我们不必使我们的功能完全安全
  • IO monad的"try/catch"控制流机制(IO是一个sin bin,为什么不在罪恶列表中抛出 try/catch?)

可能还有其他解释.

另请参阅IO Monad中的Haskell报告>基本输入/输出>异常处理

*我实际问过#haskell irc他们是否批准了这个声明.我得到的唯一回应"wonky" :)是,如果没有异议,这显然是正确的.


[编辑]请注意error并按undefined以下方式定义throw:

error :: [Char] -> a
error s = throw (ErrorCall s)

undefined :: a
undefined =  error "Prelude.undefined"
Run Code Online (Sandbox Code Playgroud)

  • 在倒数函数中使用除"错误"之外的其他异常是我认为异常的错误用法.永远不应该使用该值调用该函数,并且程序永远不应该尝试从中捕获异常,如果这样,我们的程序中存在逻辑缺陷.在这种情况下,我更喜欢使用'error'来杀死程序,以检测错误.如果使用异常处理以静默方式处理错误,则可能会在以后导致其他错误. (3认同)
  • @telephone使用`error`并不是什么神奇的东西:你可以捕获抛出的`ErrorCall`异常并静默处理它.`让foo a =错误"rawr"在Control.Exception.Base.catch(foo())(\(ErrorCall msg) - > putStrLn msg)`打印消息"rawr" (3认同)

Mat*_*hid 5

"错误"函数用于函数接收无效输入时,或者当内部发生应该永远不会发生的事情时(即错误).简而言之,调用"error"代表一个错误 - 无论是在调用者还是被调用者中.

对于不应该使用的值,"未定义"常量更多 - 通常是因为它们将被其他东西替换,或者因为它们是用于获取特定类型的幻像值.(它实际上是作为对"错误"的调用实现的.)

那么为什么我们拥有Control.Exception它的所有功能呢?

基本上,"因为I/O操作可以抛出异常".您可以愉快地通过TCP套接字与FTP服务器通话,突然连接断开.结果?您的程序抛出异常.或者你可能用完RAM,或者磁盘可能会填满,或者其他什么.

请注意,几乎所有这些都不是你的错.如果你能预料到某个特定的事情会出错,你应该使用类似的东西Maybe并Either以纯粹的方式处理它.(例如,如果你要反转一个矩阵,那么矩阵可能是不可逆的,所以你最好返回一个Maybe Matrix结果.)对于你无法合理预期的事情(例如,其他一些程序)刚刚删除了你正在尝试处理的文件),例外就是这样.

请注意,它Control.Exception包含许多用于处理异常的内容,以及仅定义许多不同类型的内容.我不在乎我调用的代码是否做了不正确的事情,因此是一个错误; 我仍然希望能够告诉客户我刚才正在谈论连接即将关闭,将描述记录到某个地方的日志文件,并做其他清理工作,而不是只是突然有我的程序,你知道,停下来.


tel*_*one 0

似乎这个问题实际上在这里讨论过:http://haskell.org/haskellwiki/Exception 我不知道这个问题是否真的可以回答,正如指出的那样。