这是我第一次认识Monad变形金刚,所以答案可能很明显。
假设我在StateT MyMonad MyType类型的do块中,我想使另一个相同类型的函数既修改状态又返回MyMonad MyType类型的值。我怎样才能做到这一点?我认为示例here在guessSession中显示它,但我似乎无法理解如何应用它!
答案 0 :(得分:9)
如果要在monad转换器中使用基础monad,可以使用lift
:
lift :: (MonadTrans t, Monad m) => m a -> t m a
在这种情况下,t
为StateT MyState
,m
为MyMonad
。所以,例如:
foo :: StateT MyState MyMonad MyType
foo = do
modify $ \s -> s+1
lift $ doSomethingInMyMonad 42
Monad变换器不是“分层”,因为你从内部返回MyMonad MyType
类型的值;它是一个更文字的转换:它们将monad变成一个新的monad,它能够在变换后的monad中运行动作。因此,您可以将StateT s m
视为常规State s
monad,除了您还可以使用lift
在m
中执行转弯操作到StateT s m
中的操作}。
如果您使用StateT
,ReaderT
等标准Monad Transformer Library(mtl)变换器,则实际上不必使用lift
;像modify
和ask
之类的东西在任何 monad中工作,并且堆栈中的某个地方有正确的变换器。 (堆栈只是一个变换的monad塔,如StateT s (ReaderT r IO)
。)
此外,如果您的底部有一个带有IO
的大堆栈,则可以使用便捷功能将IO
操作提升到任意数量的层:
liftIO :: (MonadIO m) => IO a -> m a
因此liftIO (putStrLn "Hello, world!")
适用于IO
,StateT Int IO
,ContT r (WriterT [String] IO)
,等等。
(另外请注意,foo
这里实际上并不是一个功能;更准确的术语是动作或计算。)