当Exc是成员时,Control.Eff的MonadPlus实例

时间:2014-11-11 23:13:15

标签: haskell

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。

提前感谢您的帮助。

1 个答案:

答案 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堆栈的具体布局。有了可扩展的效果,没有什么可隐藏的。不再需要拐杖。