3

单子变换器中,我们有

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

有2个问题:

  • 对于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

  • for mplus,undefined应该换成 的反函数runExc,但是在extensible-effects的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 。

预先感谢您的帮助。

4

1 回答 1

4

我认为您可能对可扩展效果感到困惑,而所需的实例MonadPlus (Eff (Exc e :> r))显示了这种困惑。

如果您希望构建一个非确定性计算并抛出异常,您可以在不需要新实例的情况下已经这样做。我想我可能通过定义单独的 mzero' 和 mplus' 来对混淆负责,这与 MonadPlus 中的完全等效。无论如何,由于这种等价性,您可以简单地写

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

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

因此,要使用非确定性和异常,您只需使用 mplus、mzero(或 mplus'、mzero')和 throwExc。无需定义任何新实例 - 与 Monad Transformers 形成鲜明对比。

当然,你必须决定你希望你的异常如何与非确定性交互:异常应该丢弃所有选择还是只丢弃剩余的选择?这取决于您如何订购处理程序,首先处理哪种效果。此外,您可以为 Choose 和 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;它成功完成,它的结果被返回。如果两者都失败,则会引发映射的异常。代码直接符合英文规范。

我们在签名中使用 MemberU2 而不是 Member,以确保计算只抛出一种类型的异常。否则,这种结构不是很有用。我使用了原始实现 Eff.hs。该文件还包含测试用例。

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

于 2014-11-14T10:04:12.207 回答