当我编写一些可能失败的函数时:
somefun :: (Monad m, ...) -> ... -> m a
somefun ... =
...
fail "some error"
我可以使用
fail
失败。但我也可以重写这个函数来使用 MonadThrow
, 所以:somefun :: (MonadThrow m, ...) -> ... -> m a
somefun ... =
...
throwM "Some error"
所以,今天我们得到了
MonadFail
, 我们也有 Monad
与 fail
,从另一个角度来看,我可能会失败 throwM
.在 LTS-11.7 中编写此类函数的正确方法是什么? throwM
有什么好处?与 fail
(因为有一种方法和另一种方法的库 - 用其他方法)?编辑:
还有当我看到this我无法理解 - 这是暂时的解决方法,但在 future 的版本中
fail
将从 Monad
中完全删除?
最佳答案
失败的方式有很多种,但最终所有的失败都转化为三类:
Maybe
或 Either
单子(monad)。这是处理故障的最佳方法,但并非总是可行IO
之类的地方。单子(monad)。 以下是一些失败的方法,应该避免:
undefined
- 当评估时被转换为运行时异常。最糟糕的失败方式,仅作为一些现有函数的参数而不会被评估,例如。 sizeOf
, alignment
等。这类函数应该写成 Proxy
相反,但这是正交的。 error
- 也转化为运行时异常。应该只在不可能发生的不可能的情况下使用。 throw
- 与 error
相同,但允许抛出特定的异常。也应该避免,因为由于懒惰,它可能会在您最不期望的地方进行评估。 fail
- 对于大多数 monads 实现是抛出 error
(默认实现)。正如@chepner 所指出的,它是为模式匹配失败而设计的,不应该真正使用。尽管如此,它仍然很受欢迎,尤其是在解析方面。 应该避免上述所有情况,因为它们的使用会导致纯代码的运行时异常。
正确的失败方法:
Maybe
, Either
, Validation
等完全失败,无一异常(exception)。 throwIO
- 在 MonadIO
中抛出异常的正确方法throwSTM
- 如果您在 STM
中,则正确抛出异常的方法. throwM
- 有一个适当的失败实现,它依赖于一个具体的 Monad
.换句话说,它将如何失败的决定推迟给函数的用户,这可以是纯的也可以不是,取决于 monad。 结束前言,让我们来看看实际的问题。
这是一个很好的例子说明为什么
fail
很糟糕,在 MonadFail 提案实现之前:λ> let unsafeDiv x y = if y == 0 then fail "Division by zero" else pure (x `div` y)
λ> 5 `unsafeDiv` 0 :: Maybe Int
Nothing
λ> 5 `unsafeDiv` 0 :: Either String Int
*** Exception: Division by zero
λ> 5 `unsafeDiv` 0 :: IO Int
*** Exception: user error (Division by zero)
STM
是另一个示例,其中 fail
真的很糟糕,因为它会导致调用默认实现:errorWithoutStackTrace :: [Char] -> a
. (见 throwSTM
为什么它不好)所以对于
fail
我们不仅会得到不同的异常,还会得到不正确的行为。另一方面,我们有
MonadThrow
:λ> let safeDiv x y = if y == 0 then throwM DivideByZero else pure (x `div` y)
λ> 5 `safeDiv` 0 :: Maybe Int
Nothing
λ> 5 `safeDiv` 0 :: Either SomeException Int
Left divide by zero
λ> 5 `safeDiv` 0 :: IO Int
*** Exception: divide by zero
如果 monad 支持它的传播,我们将总是得到相同的异常。因此,我们总是可以捕获抛出的异常。它保证了排序,所以异常不会因为懒惰而逃逸。我认为,对您的问题最正确的答案是使用特定于您所在的 monad 的失败方法,但是如果您不提前知道确切的 monad,并且想让您的功能的用户要选择如何失败,请前往
throwM
在相关主题上,我建议不要使用 MonadCatch
而是使用 unliftio
之类的东西或 safe-exceptions
.查看有关异常处理的更多信息 here .
关于haskell - MonadCatch 有什么好处?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57076728/