在monad transformers中,我们有
instance (Monad m, Monoid e) => MonadPlus (ExceptT e m)
在extensible effects中,没有
这样的东西instance (Monoid e) => MonadPlus (Eff (Exc e :> r))
我试图实施它,徒劳无功。以下是我到目前为止的情况:
instance (Monoid e) => MonadPlus (Eff (Exc e :> r)) where
mzero = throwExc mempty
a `mplus` b = undefined $ do
resultA <- runExc a
case resultA of
Left l -> runExc b
Right r -> return $ Right r
有两个问题:
mzero
,,GHC抱怨如下:
Could not deduce (Monoid e0) arising from a use of ‘mempty’
from the context (Monad (Eff (Exc e :> r)), Monoid e)
为什么GHC与e0
匹配e
?
回答(在评论中提供):启用ScopedTypeVariables
mplus
,,undefined
应替换为runExc
的反函数,但我无法在可扩展效果的API中找到它。我错过了什么吗?
理由:我希望能够在a <|> b
内撰写Member (Exc e) r => Eff r a
,意思是:
a
a
抛出ea
,请尝试b
b
抛出eb
,则抛出mappend ea eb
这需要一个Alternative
实例,这就是我尝试首先实现MonadPlus
实例的原因。
注意:我使用的是GHC 7.8.3。
提前感谢您的帮助。
答案 0 :(得分:4)
我认为你可能会对可扩展效果和期望效果感到困惑
实例MonadPlus (Eff (Exc e :> r))
显示混乱。
如果您希望构建非确定性计算并且还要抛出 例外情况,你可以做到这一点,而不需要新的 实例。我想我可能会对这种混乱负有部分责任 定义单独的mzero&#39;和mplus&#39;完全等同于 那些在MonadPlus。无论如何,由于这种等价, 你可以简单地写一下
instance Member Choose r => MonadPlus (Eff r) where
mzero = mzero'
mplus = mplus'
实例说:具有选择效果的计算 其他,是MonadPlus计算的一个实例。让我强调一下 部分“在其他人中&#39;&#39;。计算可能有其他影响, 例如,抛出异常。上面的MonadPlus实例涵盖了 那个案子,以及所有其他案件。
因此,要使用非确定性和异常,您只需使用mplus,mzero (或mplus&#39;,mzero&#39;)和throwExc。没有必要定义 任何新情况 - 与Monad变形金刚形成鲜明对比。
当然,您必须决定您希望异常如何进行交互 具有非决定论:如果异常丢弃所有选择或仅丢弃 剩下的选择?这取决于您订购处理程序的方式 效果首先被处理。此外,您可以为两者编写处理程序 选择和Exc效果(保持已经做出的选择 例外并丢弃剩余的 - 从而模拟Prolog的剪辑)。 图书馆的代码(以及论文附带的代码)都有这样的例子。
编辑回复修正后的问题:
如果你需要的只是<|>
,它可以简单地实现,不需要MonadPlus或cut。
该运算符仅仅是一种异常处理形式,可以实现
两个catchError组成。这是完整的代码
alttry :: forall e r a. (Typeable e, Monoid e, MemberU2 Exc (Exc e) r) =>
Eff r a -> Eff r a -> Eff r a
alttry ma mb =
catchError ma $ \ea ->
catchError mb $ \eb -> throwError (mappend (ea::e) eb)
如果计算ma成功完成,则返回其结果。除此以外, mb尝试过;它成功完成,其结果返回。如果两者都失败了 提出了mappend-ed异常。代码直接匹配英文规范。
我们在签名而不是会员中使用MemberU2 确保计算仅抛出一种类型的异常。 否则,这种结构不是很有用。我用过原来的实现 Eff.hs。该文件还包含测试用例。
BTW,具有可扩展的效果,没有 需要定义或使用类型类,如MonadPlus,MonadState等。这些类型类 旨在隐藏MonadTransformer堆栈的具体布局。有了可扩展的效果,没有什么可隐藏的。不再需要拐杖。