【问题标题】:MonadPlus instance for Control.Eff when Exc is member当 Exc 是成员时 Control.Eff 的 MonadPlus 实例
【发布时间】:2014-11-11 23:13:14
【问题描述】:

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

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

  • 对于mplusundefined应该换成runExc的反函数,但是在extensible-effects的API中找不到。我错过了什么吗?

基本原理:我希望能够在Member (Exc e) r =&gt; Eff r a内写a &lt;|&gt; b,意思是:

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

这需要一个Alternative 实例,这就是为什么我首先尝试实现一个MonadPlus 实例。

注意:我使用的是 GHC 7.8.3。

提前感谢您的帮助。

【问题讨论】:

  • “为什么 GHC 不匹配 e0 和 e” - 打开 ScopedTypeVariables
  • @user2407038 确实解决了第一个问题,谢谢!

标签: haskell monadplus effect-systems


【解决方案1】:

我认为您可能对可扩展效果和所需的效果感到困惑 实例MonadPlus (Eff (Exc e :&gt; 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 形成鲜明对比。

当然,你必须决定你希望你的异常如何交互 具有非确定性:异常应该放弃所有选择还是只 剩下的选择?这取决于您如何订购处理程序,其中 先处理效果。此外,您可以为两者编写一个处理程序 选择和执行效果(将已经做出的选择保留在 异常并丢弃剩余的——从而对 Prolog 的剪辑进行建模)。 库的代码(以及论文随附的代码)有这样的例子。

编辑以回复修改后的问题: 如果你只需要&lt;|&gt;,它可以简单地实现,无需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 堆栈的具体布局。有了可扩展的效果,就没有什么可隐藏的了。不再需要拐杖了。

【讨论】:

  • 好的,我需要更多的阅读和测试来理解 Control.Eff.Cut。与此同时,我已经用我的基本目标更新了这个问题,即利用Alternative 中的&lt;|&gt; 运算符。 Prolog 的剪切模型会帮助我做到这一点吗?
  • 关于“不需要定义或使用像 MonadPlus、MonadState 这样的类型类”:我知道Eff r a 已经隐藏了底层联合类型r 的具体元素,但是我仍然有兴趣使用 Monad* 类型类进行名称重载。 a &lt;|&gt; b 看起来比 a `alttry` b 更好,你不觉得吗?无论如何,谢谢你的详细回答,它有很大帮助。
猜你喜欢
  • 1970-01-01
  • 2016-08-03
  • 2018-11-06
  • 2015-08-23
  • 1970-01-01
  • 1970-01-01
  • 2019-05-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多