Search code examples
haskellmonadsmonad-transformersreader-monad

Is there ReaderT raised into a monad?


I've written the following monad transformers (which I believe are equivalent to each other), namely:

newtype MonadReaderT1 r m a = MonadReaderT (ReaderT (r m) m a)
newtype MonadReaderT2 r m a = MonadReaderT (ReaderT (m r) m a)

The purpose of these is that I basically want a ReaderT, but my environment has to be accessed inside the Monad, it's not actually a fixed pure value (in my case, it's an auth token that needs to be periodically refreshed).

The reason why I think MonadReaderT1 and MonadReaderT2 are equivalent, because I can just go:

newtype B m = B (m A)

And then MonadReaderT1 B is the same as MonadReaderT2 A.

But I think I need this extra machinery here above and beyond what I get with plain old ReaderT.

But I get the feeling I'm not the first person to have done this or needed this. Have I just reinvented an existing type, and if so what is it?


Solution

  • I'm not sure it's worth defining a separate transformer in this case. Consider that if you were using a reader transformer with an Int environment, you probably wouldn't feel a need to write a special transformer for this case:

    newtype IntReaderT m a = IntReaderT (ReaderT Int m a)
    

    Instead, you'd just use the ReaderT Int directly.

    Similarly, if you happen to want a monadic action as your environment, I think you should just use the ReaderT directly, defining your monad stack as either a type:

    type M = ReaderT (IO Token) IO
    

    or a newtype:

    newtype M a = M { unM :: ReaderT (IO Token) IO a }
    

    along with a helper to fetch the token:

    token :: M Token
    token = ask >>= liftIO
    

    If you prefer to write in a style that makes heavy use of mtl constraints, you can instead define token using a class and instance:

    class MonadToken token m where
      token :: m token
    instance MonadToken Token M where
      token = ask >>= liftIO
    

    which allows you to write generic token-using monadic actions:

    authorized :: (MonadToken token m) => m Bool
    authorized = do
      t <- token
      ...
    

    without actually having to define a new transformer.