【问题标题】:Requires MonadPlus (ST a) Instance需要 MonadPlus (ST a) 实例
【发布时间】:2011-10-06 22:55:17
【问题描述】:

我正在阅读论文Typed Logical Variables in Haskell,但我无法理解最终实现的细节。特别是第 4 节中介绍的回溯状态转换器。出于某种我不知道的原因,GHC 认为我需要在函数unify 中为(ST a) 提供MonadPlus 实例,如下所示:

newtype BackT m a = BT { run :: forall b . (a -> m [b]) -> m [b] }

instance (Monad m) => Monad (BackT m) where
 return a   = BT (\k -> k a)
 BT m >>= f = BT (\k -> m (\a -> run (f a) k))

instance (MonadPlus m) => MonadPlus (BackT m) where 
 mzero             = BT (\s -> mzero)
 f `mplus` g = BT (\s -> (run f) s `mplus` (run g) s)

type LP a = BackT (ST a)
type LR   = STRef

type Var s a = LR s (Maybe a)   
data Atom s = VarA (Var s (Atom s)) | Atom String

class Unify b a | a -> b where
 var   :: a -> Maybe (Var b a)
 unify :: a -> a -> LP s ()

instance Unify s (Atom s) where
 var (VarA a) = Just a
 var _        = Nothing

 unify (Atom a) (Atom b) | a == b = return () -- type checks
 unify _        _                 = mzero     -- requires MonadPlus (ST a) instance

我不确定问题是什么,以及如何解决它。到目前为止,我的印象是我理解了前面的讨论和代码,但显然我错了。如果有人可以指出出了什么问题-我是否需要MonadPlus (ST a) 实例? - 这会很有帮助。

[编辑:澄清]我应该指出 作者 似乎声称 mzeromzero 的一些变体是适当的功能.我只是不知道合适的功能是什么。我想知道是我应该创建一个MonadPlus (ST a) 实例还是我没有使用正确的函数,并且误读了一些东西。

【问题讨论】:

  • unify的返回类型是LP s (),或者BackT (ST a) ()。所以显然BackT mMonadPlus 实例需要MonadPlus m。您能在此处包含该实例吗?

标签: haskell monad-transformers unification st-monad monadplus


【解决方案1】:

mzero 是类型类MonadPlus 的成员。特别是

mzero :: MonadPlus m => m a

用于您的函数unify 的monad 是LP,它实际上是用ST 实例化的BackT。您还为BackT 定义了一个MonadPlus 的实例,它依赖于底层monad 的这样一个实例。由于ST 没有这样的实例,GHC 嘲笑你。

这是重要的部分:

instance (MonadPlus m) => MonadPlus (BackT m) where 
  mzero             = BT (\s -> mzero)
  f `mplus` g = BT (\s -> (run f) s `mplus` (run g) s)

简单地说:这是BackT mMonadPlus 实例,前提是m 也是MonadPlus 的实例。由于m 是用ST 实例化的,因此您需要ST 的实例。我想知道您如何在没有委托的情况下定义MonadPlus 的合理实例。我有个主意:

instance MonadPlus (BackT m) where
  mzero = BT (const $ return [])
  mplus (BT f) (BT g) = BT $ \a -> do
    fs <- f a
    gs <- g a
    return $ fs ++ gs

这个实例基本上连接了两个输出列表。我希望它适合您的需求。

【讨论】:

  • 你说得对;我已经更新了我的帖子以包含定义。可能我误解了 monad 并错误地定义了 bind、return、mzero 或 plus。
  • 这非常有效。感谢您解释问题和解决方案。
  • 小心!这个 MonadPlus 定义可能不是你所期望的。在最终失败的分支中对STRefs 的中间写入将不会回滚。
  • @luqui 是正确的;如果您尝试在一个分支中使用常规的writeSTRef,它的效果将在另一个分支中看到。为避免这种情况,请使用第 4.2 节中的writeLPRef 定义。它将BackT 延续包装在本质上是失败延续中,以使mplusST 看起来可交换。不过,这只是纪律问题,而不是 ST 真的是一个表现良好的 MonadPlus 实例。
【解决方案2】:

BackTMonadPlus 实例可能应该使用[]MonadPlus 实例而不是m,如下所示:

instance (Monad m) => MonadPlus (BackT m) where 
  mzero       = BT (\_ -> return mzero)
  f `mplus` g = BT (\s -> liftM2 mplus (run f s) (run g s))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多