如果两个monad变换器属于不同类型,但它们的底层monad是同一类型,是否有一种原则性的方法来组成它们?

时间:2013-08-21 18:14:48

标签: haskell monads monad-transformers

我无法解决问题。但这是一个用例:假设你有两个monad变换器,ts,转换同一个monad m

master :: (MonadTrans t, Monad m) => t m a b
slave  :: (MonadTrans t, Monad m) => s m a b

我希望撰写masterslave,以便在m原语被提升到ts时,他们可以相互沟通。签名可能是:

bound :: (MonadTrans t, MonadTrans s, Monad m, Monoid a) => t m a b -> s m a b -> (...)
But what is the type of (...) ?

用例,用含糖符号表示:

master :: Monoid a => a -> t m a b
master a = do 
   a <- lift . send $ (a,False)     -- * here master is passing function param to slave
   ...                              -- * do some logic with a
   b <- lift . send $ (mempty,True) -- * master terminates slave, and get back result

slave :: Monoid a => (a -> b) -> s m a b
slave g = do 
    (a,end) <- lift receive
    case end of 
        True -> get >>= \b -> exit b  
        _    -> (modify (++[g a])) >> slave g

更新:sendreceivem类型的原语。

我很抱歉,如果这个例子看起来很人为,或者过于类似协程,问题的精神真的与它无关,所以请忽略所有的相似之处。但主要的一点是,monads ts之前不能合理地相互组合,但是在两个底层monad m之后,它们现在可以组成并运行为单一功能。至于组合函数的类型,我真的不确定,所以一些方向是值得赞赏的。现在如果这种抽象已经存在而且我只是不知道它,那么这将是最好的。

1 个答案:

答案 0 :(得分:8)

是。将hoist包中的mmorphlift结合使用即可:

bound
    :: (MonadTrans t, MonadTrans s, MFunctor t, Monad m)
    => t m () -> s m () -> t (s m) ()
bound master slave = do
    hoist lift master
    lift slave

要了解其工作原理,请研究hoist的类型:

hoist :: (MFunctor t) => (forall x . m x -> n x) -> t m r -> t n r

hoist允许您修改任何实现MFunctor的monad变换器的基本monad(大部分都是这样)。

bound的代码所做的是让两个monad变换器就最终目标monad达成一致,在这种情况下是t (s m)。您嵌套ts的顺序取决于您,所以我假设您想在外面t

然后,只需使用hoistlift的各种组合来使两个子计算在最终的monad堆栈上达成一致。第一个是这样的:

master :: t m r
hoist lift master :: t (s m) r

第二个是这样的:

slave :: s m r
lift slave :: t (s m) r

现在他们都同意,所以我们可以在同一个do块内对它们进行排序,它将“正常工作”。

要详细了解hoist的工作原理,建议您查看the documentation,了解底部有a nice tutorialmmorph包。