可以说我有一个功能
f :: State [Int] Int
和一个功能:
g :: StateT [Int] IO Int
我想在它们之间使用f
并g
传递状态。有库函数
StateT (return . runState f)
吗?或者一般来说,给定一个具有相应 monad 的 monad 转换器,是否有它的库函数?
可以说我有一个功能
f :: State [Int] Int
和一个功能:
g :: StateT [Int] IO Int
我想在它们之间使用f
并g
传递状态。有库函数
StateT (return . runState f)
吗?或者一般来说,给定一个具有相应 monad 的 monad 转换器,是否有它的库函数?
StateT m
您要的是从 monad到的映射(称为 monad 态射)StateT n
。我将使用这个mmorph
库,它提供了一组非常好的工具来处理单子态射。
为了执行State -> StateT m
您正在寻找的转换,我们将首先定义一个态射来概括Identity
嵌入的 monad State
,
generalize :: Monad m => Identity a -> m a
generalize = return . runIdentity
接下来我们要提升这个态射来作用于你的内部单子StateT
。也就是说,我们想要一个函数,它给出从一个单子到另一个单子的映射(例如我们的generalize
态射),将给我们一个作用于单子转换器的基本单子的函数,例如t Identity a -> t m a
。你会发现这类似于's类的hoist
功能,mmorph
MFunctor
hoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b
将碎片拼凑在一起,
myAction :: State s Int
myAction = return 2
myAction' :: Monad m => StateT s m Int
myAction' = hoist generalize myAction
更一般地说,您要做的是将转换应用于变压器堆栈的内层。对于两个任意 monad,类型签名可能如下所示:
fmapMT :: (MonadTrans t, Monad m1, Monad m2) => (m1 a -> m2 a) -> t m1 a -> t m2 a
基本上是更高级别的fmap
。事实上,将它与最终参数上的映射结合起来可能更有意义:
fmapMT :: (MonadTrans t, Monad m1, Monad m2) => (m1 a -> m2 b) -> t m1 a -> t m2 b
显然,这并非在所有情况下都是可能的,尽管当“源”monadIdentity
可能更容易时,但我可以想象为它工作的地方定义另一个类型类。我认为典型的 monad 转换器库中没有这样的东西。然而,一些关于 hackage 的浏览在包中发现了一些非常相似的Monatron
东西:
class MonadT t => FMonadT t where
tmap' :: FunctorD m -> FunctorD n -> (a -> b)
-> (forall x. m x -> n x) -> t m a -> t n b
tmap :: (FMonadT t, Functor m, Functor n) => (forall b. m b -> n b)
-> t m a -> t n a
tmap = tmap' functor functor id
在 for 的签名中tmap'
,FunctorD
类型基本上是临时实现,fmap
而不是直接使用Functor
实例。
此外,对于两个类似 Functor 的类型构造函数 F 和 G,具有 like 类型的函数(forall a. F a -> G a)
描述了从 F 到 G 的自然转换。很可能在包中的某处您想要的转换器映射的另一种实现,category-extras
但我不确定monad 转换器的类别理论版本是什么,所以我不知道它可能被称为什么。
由于tmap
只需要一个Functor
实例( anyMonad
必须具有)和一个自然转换,并且 anyMonad
具有从Identity
提供的 monad的自然转换,因此可以为 as的return
任何实例一般地编写您想要的函数—— 假设“基本”monad 是无论如何,定义为应用于 的转换器的同义词,转换器库通常就是这种情况。FMonadT
tmap (return . runIdentity)
Identity
回到您的具体示例,请注意 Monatron 确实有一个FMonadT
for的实例StateT
。
并非所有单子转换器都可以定义这样的功能。Cont r
例如,不能提升 monad,因为这ContT r IO
需要将 IO monad ( a -> IO r
) 中的延续转换为纯延续 ( a -> r
)。