如果你想在一个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 中运行动作。因此,您可以将StateT s m 视为常规的State s monad,除了您还可以使用lift 将m 中的操作转换为StateT s m 中的操作。
如果您使用标准的Monad Transformer Library (mtl) 转换器,例如StateT、ReaderT 等,您实际上不必使用lift; modify 和 ask 之类的东西在 any 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 这里实际上并不是一个函数;更准确的术语是 action 或 computation。)