这是我第一次认识 Monad Transformers,所以答案可能很明显。
假设我在 StateT MyMonad MyType 类型的 do 块内,我想让另一个相同类型的函数既修改状态又返回 MyMonad MyType 类型的值。我怎样才能做到这一点?我认为这里的示例在guessSession中显示了它,但我似乎无法理解如何应用它!
这是我第一次认识 Monad Transformers,所以答案可能很明显。
假设我在 StateT MyMonad MyType 类型的 do 块内,我想让另一个相同类型的函数既修改状态又返回 MyMonad MyType 类型的值。我怎样才能做到这一点?我认为这里的示例在guessSession中显示了它,但我似乎无法理解如何应用它!
如果要在 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
将 action in 运行m
为 action in之外StateT s m
。
如果您使用的是标准的Monad Transformer Library (mtl) 转换器,例如StateT
,ReaderT
等,您实际上不必使用lift
; 在堆栈中的某个地方使用正确的变压器在任何modify
单子中ask
工作。(堆栈只是转换后的 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或compute。)