我无能为力来扩展这个问题。但这里有一个用例:假设您有两个单元变压器,t
和 s
在同一个单元m
上变换:
master :: (MonadTrans t, Monad m) => t m a b
slave :: (MonadTrans t, Monad m) => s m a b
我想编写master
和slave
,以便当m
基元被提升到t
和s
时,它们可以相互通信。签名可能是:
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
更新:send
和 receive
是 m
类型的基元。
如果这个例子看起来很做作,或者太像协程,我深表歉意,问题的精神真的与它无关,所以请忽略所有相似之处。但重点是,monad t
和 s
以前不能合理地相互组合,但在两者都包裹了一些底层 monad m
之后,它们现在可以作为单个函数组合和运行。至于组合函数的类型,我真的不确定,所以一些方向是赞赏的。现在,如果这种抽象已经存在,而我只是不知道它,那么那将是最好的。
是的。 将 mmorph
包中的hoist
与lift
相结合以执行此操作:
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
的任何单元变压器(大多数(的基本单元转换器。
bound
的代码所做的是让两个单子变压器就最终目标单子达成一致,在本例中为t (s m)
。 你嵌套t
和s
的顺序取决于你,所以我只是假设你想要t
在外面。
然后,只需使用hoist
和lift
的各种组合即可使两个子计算就最终的monad堆栈达成一致。 第一个工作原理如下:
master :: t m r
hoist lift master :: t (s m) r
第二个工作原理如下:
slave :: s m r
lift slave :: t (s m) r
现在他们都同意,所以我们可以在同一个do
块中对它们进行排序,它将"正常工作"。
要了解有关hoist
工作原理的更多信息,我建议您查看mmorph
包的文档,该文档底部有一个很好的教程。