Exc为成员时Control.Eff的MonadPlus实例



在monad变压器中,我们有

instance (Monad m, Monoid e) => MonadPlus (ExceptT e m)

在可扩展效果中,没有这样的东西

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不匹配e0e

    答案(在评论中提供):打开ScopedTypeVariables

  • 对于mplus,应将undefined替换为runExc的反函数,但我在可扩展效应的API中找不到它。我错过什么了吗?

理由:我希望能够在Member (Exc e) r => Eff r a中写入a <|> b,意思是:

  • 尝试a
  • 如果a抛出ea,请尝试b
  • 如果b抛出eb,则抛出mappend ea eb

这需要一个Alternative实例,这就是我首先尝试实现MonadPlus实例的原因。

注意:我使用的是GHC 7.8.3.

提前感谢您的帮助。

我认为您可能对可扩展效果和所需效果感到困惑实例CCD_ 19显示了混淆。

如果您希望构建一个非确定性计算,并同时抛出例外,您可以在不需要任何新的实例。我想我可能对这种混乱负有部分责任定义单独的mzero和mplus,它们完全等价于那些在MonadPlus。无论如何,因为这种等价性,你可以简单地写

instance Member Choose r => MonadPlus (Eff r) where
    mzero = mzero'
    mplus = mplus'

实例说:一个具有Choose效应的计算,在其他,是MonadPlus计算的一个实例。让我强调一下"除其他外"部分。该计算可以具有其他效果,例如,抛出异常。上面的MonadPlus实例涵盖这种情况,以及所有其他情况。

因此,要使用非决定论和异常,只需使用mplus、mzero(或mplus',mzero')以及throwExc。没有必要定义任何新的例子-与莫纳德变形金刚形成鲜明对比。

当然,你必须决定你希望你的异常如何交互使用非决定论:异常应该放弃所有选择还是只放弃剩下的选择?这取决于您如何订购处理程序效果首先得到处理。此外,您可以为这两者编写一个处理程序选择和豁免效果(将已经做出的选择保留在异常并丢弃剩余的——从而对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而不是Member确保计算只抛出一种类型的异常。否则,这种构造就没有多大用处。我使用了原始实现该文件还包含测试用例。

BTW,具有可扩展的效果需要定义或使用MonadPlus、MonadState等类型类。这些类型类旨在隐藏MonadTransformer堆栈的具体布局。有了可扩展的效果,就没有什么可隐藏的了。不再需要拐杖了。

最新更新