【问题标题】:Combining StateT and State monads结合 StateT 和 State monad
【发布时间】:2011-05-07 12:33:50
【问题描述】:

假设我有一个函数

f :: State [Int] Int

还有一个功能:

g :: StateT [Int] IO Int

我想在g 中使用f 并在它们之间传递状态。
StateT (return . runState f) 有库函数吗?或者一般来说,给定一个具有相应 monad 的 monad 转换器,是否有它的库函数?

【问题讨论】:

标签: haskell monads monad-transformers state-monad


【解决方案1】:

这样的函数并不是所有的单子转换器都可以定义的。例如,Cont r monad 不能提升为 ContT r IO,因为这需要将 IO monad (a -> IO r) 中的延续转换为纯延续 (a -> r)。

【讨论】:

  • 没想到。就像你说的,这对每个 monadtransformer 都是不可能的。所以它需要在变压器和相应的单子之间建立一种特殊的连接......
  • 转型不就反过来了吗?由于延续类型中的r 通常是多态的,你可以写成(ContT . runCont) :: Cont (m r) a -> ContT r m a
  • @camccann 我打算进行一类广义的转换(Monad m => m a -> (TransformerOf m) m' a)(有点滥用符号)。如果您尝试为 (Cont r a -> ContT r m a) 编写一个实例,您将陷入我所描述的问题。
【解决方案2】:

更一般地说,您要做的是将转换应用到转换器堆栈的内层。对于两个任意 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

显然,这在所有情况下都不可能,尽管当“源”monad 是 Identity 时可能会更容易,但我可以想象为它工作的地方定义另一个类型类。我认为典型的 monad 转换器库中没有这样的东西。然而,一些关于 hackage 的浏览会发现一些非常相似的东西 in the Monatron package:

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

tmap' 的签名中,FunctorD 类型基本上是fmap 的临时实现,而不是直接使用Functor 实例。

此外,对于两个类似 Functor 的类型构造函数 F 和 G,具有类似 (forall a. F a -> G a) 类型的函数描述了从 F 到 G 的 a natural transformation。很可能在 @ 987654334@ 包,但我不确定 monad 转换器的类别理论版本是什么,所以我不知道它可能被称为什么。

由于tmap 只需要一个Functor 实例(任何Monad 必须具有)和一个自然变换,并且任何Monad 都具有来自return 提供的Identity monad 的自然变换,因此您想要的函数可以针对FMonadT 的任何实例一般地编写为tmap (return . runIdentity)——假设“基本”monad 被定义为应用于Identity 的转换器的同义词,无论如何,这通常是变压器库。

回到您的具体示例,请注意,Monatron 确实有一个 FMonadT 实例@ 用于 StateT

【讨论】:

  • 我没有看过 Monatron 包。我得仔细看看才能判断。我确实喜欢你定义一个类型类何时起作用的想法,任何人都可以确认或不确认 Monatron 这样做吗?
【解决方案3】:

您要求的是从单子 StateT mStateT n 的映射(称为单子态射)。我将使用mmorph 库,它提供了一组非常好的工具来处理单子态射。

要执行您正在寻找的State -> StateT m 变换,我们将首先定义一个态射来泛化嵌入在State 中的Identity monad,

generalize :: Monad m => Identity a -> m a
generalize = return . runIdentity

接下来我们要提升这个态射来作用于StateT 的内部单子。也就是说,我们想要一个函数,它给出从一个单子到另一个单子的映射(例如我们的generalize 态射),将给我们一个作用于单子转换器的基本单子的函数,例如t Identity a -> t m a。你会发现这类似于mmorphMFunctor 类的hoist 函数,

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

【讨论】:

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