假设我有一个 monadT:
type Wrap a = ReaderT Env ( StateT Int ( StateT Int Identity ) ) a
这里要注意的重要一点是,一个 StateT 包装了另一个,并且两者都包装在第三个 MonadT 中,即 ReaderT。
以及相应的 runWrap 函数为方便起见:
type Env = Map.Map Char Integer
runWrap :: Env -> Int -> Int -> Wrap a -> a
runWrap env st1 st2 m = runIdentity $ evalStateT ( evalStateT ( runReaderT m env ) st2 ) st1
还有一个通用的 tock 状态单子:
tock :: (Num s, MonadState s m) => m ()
tock = do modify (+1)
我现在创建一个 wrap monadT,其中我使用 tock:
aWrap :: Wrap ( Int, Int )
aWrap = do
lift tock
lift . lift $ tock
x <- get
y <- lift . lift $ get
return ( x, y )
并运行它:
env = Map.fromList [('x', 1)]
runWrap env 1 200 aWrap
// answer: (201,2)
lift
就我对如何与 MonadT 的嵌套层进行交互的理解而言,这里的使用对我来说很有意义。
但是,这也有效并给了我相同的答案(201,2)
:
aWrap :: Wrap ( Int, Int )
aWrap = do
tock
lift . lift $ tock
x <- get
y <- lift . lift $ get
return ( x, y )
我认为通过调用tock
w/o lift
,它看起来好像tock
应用于外部 MonadT,即 ReaderT,这是没有意义的。但为什么这行得通?
PS 请忽略Env
这里的存在,它与问题无关,只是我正在使用的外部 MonadT 的选择。