Let's say I have a monadT:
type Wrap a = ReaderT Env ( StateT Int ( StateT Int Identity ) ) a
The important thing to note here is that one StateT is wrapping another, and both are wrapped inside a third MonadT, namely ReaderT.
and the corresponding runWrap function for convenience:
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
And a generic tock state monad:
tock :: (Num s, MonadState s m) => m ()
tock = do modify (+1)
I now create a wrap monadT where inside I use tock:
aWrap :: Wrap ( Int, Int )
aWrap = do
lift tock
lift . lift $ tock
x <- get
y <- lift . lift $ get
return ( x, y )
And run it:
env = Map.fromList [('x', 1)]
runWrap env 1 200 aWrap
// answer: (201,2)
The use of lift
here makes sense to me in terms of my understanding of how to interact w/ nested layers of MonadT.
However, this also works and give me the same answer: (201,2)
:
aWrap :: Wrap ( Int, Int )
aWrap = do
tock
lift . lift $ tock
x <- get
y <- lift . lift $ get
return ( x, y )
I would think by calling tock
w/o lift
, it reads as if tock
is applied to the outer MonadT, namely ReaderT, which makes no sense. But why does this work?
P.S. Please ignore the presence of Env
here, it has nothing to do w/ the question, just the choice of the outer MonadT I'm using.