MonadPlus IO不是一个简单的东西 [英] MonadPlus IO isn't a monoid
问题描述
实例 MonadPlus IO
是唯一的,因为 mzero
抛出:
Instance MonadPlus IO
is unique because mzero
throws:
Prelude Control.Monad> mzero
*** Exception: user error (mzero)
因此, MonadPlus IO
暗示它也旨在解决错误.
So accordingly, MonadPlus IO
implies that it is also intended for errors.
mzero
显然可以用作标识元素:
mzero
apparently serves as the identity element if the other action doesn't throw:
Prelude Control.Monad> mzero `mplus` return 0
0
Prelude Control.Monad> return 0 `mplus` mzero
0
但是当两个动作都抛出时却没有:
But it doesn't when both actions throw:
Prelude Control.Monad> fail "Hello, world!" `mplus` mzero
*** Exception: user error (mzero)
Prelude Control.Monad> mzero `mplus` fail "Hello, world!"
*** Exception: user error (Hello, world!)
所以 MonadPlus IO
并不是一个monoid.
So MonadPlus IO
is not a monoid.
如果用户意图出错时它违反了 MonadPlus
法律,那么它的真正目的是什么?
If it violates MonadPlus
laws when user intends errors, what is it actually intended for?
推荐答案
IO
是相对于标识异常的等价类的类.没那么令人满意.一种替代方法可能如下所示:
IO
under mplus
is a monoid relative to an equivalence class that identifies exceptions. Not that satisfying. An alternative approach might look like this:
m <|> n = m `catches`
[ Handler $ \ ~EmptyIO -> n
, Handler $ \ ~se@(SomeException _) ->
n `catch` \ ~EmptyIO -> throwIO se ]
此方法的主要问题是处理程序可以堆叠.当第一个动作失败时,我们不能仅仅执行第二个动作.一个较小的问题是,没有一种完全可靠的方法来确定异常是同步的(应该使用 throwIO
抛出)还是异步的(在这种情况下,我们需要使用 throwTo 和我们自己的线程ID).这样一团糟.
The main problem with this approach is that handlers can stack up. When the first action fails, we can't just commit to the second action. A smaller issue is that there's no completely reliable way to determine whether an exception is synchronous (and should be rethrown using throwIO
) or asynchronous (in which case we need to rethrow it using throwTo
with our own thread ID). So that way lies messes.
这篇关于MonadPlus IO不是一个简单的东西的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!