Search code examples
haskellghclifting

How "auto lift" is implemented in Haskell?


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.


Solution

  • 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.