I'm new to Haskell and learning about Monad Transformer. I found that lift
can be omitted when operating on an inner monad in a monad stack. For example:
type Foo = ReaderT String (WriterT String Identity) Int
foo :: Int -> Foo
foo x = do
env <- ask
tell $ env ++ "in foo" -- actually, it should be `lift $ tell $ env ++ "in foo"` intuitively
return x
I think maybe it's not a language feature but an ad-hoc extension of ghc
(right?). And I wonder how this is implemented.
The signature of tell
is tell :: MonadWriter w m => w -> m ()
[Hackage].
It thus is implemented for any m
that is a member of the MonadWriter
, and one of these instances is:
instance MonadWriter w m => MonadWriter w (ReaderT r m) where
-- …
If the inner monad of a ReaderT
thus is a MonadWriter
(and WriterT
offers that), then ReaderT
is also a member of the MonadWriter
. It implements, as you probably figured out yourself, tell
as [Haskell-src]:
instance MonadWriter w m => MonadWriter w (ReaderT r m) where writer = lift . writer tell = lift . tell listen = mapReaderT listen pass = mapReaderT pass
so it performs a lift
itself, just because of the instance
Haskell will pick for it. The idea is that for each monad transformer a person defines, where lifting is possible, you could implement this to make it more transparent where tell
will be "routed" to.