【问题标题】:Exceptions in HaskellHaskell 中的异常
【发布时间】:2012-01-05 23:04:05
【问题描述】:

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

但有时即使是纯函数也可能抛出异常,例如read "..." :: Int(当读取字符串不代表整数时),运算符(!!)(当我们试图让项目超出列表范围时)等等。这是真实的行为,我不否认。但是,我不想更改函数的签名只是为了让它有机会捕获可能的异常,因为在这种情况下,我必须通过调用堆栈更改所有函数的签名。

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

【问题讨论】:

  • 我认为这些异常是“你有错误”的类型,因此不应该首先发现。

标签: exception haskell


【解决方案1】:

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

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

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

【讨论】:

  • 我强烈反对 IO 中的异常在 Haskell 中很好且有用。最好使用MaybeErrorT 或类似的显式方法。推理起来要简单得多;异常处理代码似乎总是与错误密切相关。它更可组合。它更易于维护。他们应该被禁止。
  • @JohnL:嗯,是的,结果有点不对。我已经修改了我的声明。不过,我认为它们不应该被禁止。有些东西,比如异步异常(虽然很痛苦),不能用ErrorT之类的东西来实现。在实践中,我通常会遇到我打算处理使用ErrorT 的错误,并且我能做的最好的事情就是在我自己使用bracket 和相关的IO 异常函数之后进行清理。
  • 异步异常肯定是个问题。我应该对我的评论进行限定,即我只指同步异常,因为我也没有看到更好的异步异常解决方案。
【解决方案2】:

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

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

head :: [a] -> Maybe a

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

所以eqHead 不能写成“纯粹”(我希望探索其替代方案的句法选择),但它也不必真正了解headMaybe-ness,它只需知道head 可能会以某种方式失败。

它并不完美,但其想法是 Haskell 中的函数与 Java 的风格不同。在典型的 Haskell 设计中,异常通常不会发生在调用链的深处。相反,当我们知道所有参数都已完全定义且行为良好,并且验证发生在最外层时,深层调用链都是纯粹的。所以从深处冒出来的异常并不是真正需要在实践中得到支持的东西。

这样做的困难是否会导致设计模式,或者设计模式会导致缺乏支持这一点的功能,这是一个争论的问题。

【讨论】:

    【解决方案3】:

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

    作为对您问题的更直接的回答,spoon

    【讨论】:

    • 请注意,spoon 确实使用unsafePerformIO。它不应该也有一些NOINLINE 撒在里面吗?或者类似的东西?
    【解决方案4】:

    我不同意那些说解决方案是“一开始就避免出现错误”的人的观点。我将围绕在这些单子之一中处理您的错误来构建您的代码。

    纯函数(和 FP)的主要优势之一是能够推理代码,类似于“如果函数具有类型 [a] -&gt; a,那么对于 所有 列表a 类型的值,我将取回 a 类型的值。”像这样的例外情况会从下面剪掉腿。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-01
      • 1970-01-01
      • 2015-06-11
      • 2011-10-30
      • 2011-07-10
      • 1970-01-01
      相关资源
      最近更新 更多