I'm pulling together code from a number of different places, and I'm trying to deal with the following:
Problem
I have a transformer stack with the following simplified type:
action :: m (ReaderT r IO) a
and I'm trying to use the action in the context of a different stack, which has a different reader environment:
desired :: m (ReaderT r' IO) a
I can of course provide
f :: r' -> r
Example
things :: m (ReaderT r' IO) ()
things = do
-- ... some stuff
-- <want to use action here>
action :: m (ReaderT r IO) a -- broken
-- ... more stuff
pure ()
What I've considered
withReaderT :: (r' -> r) -> ReaderT r m a -> ReaderT r' m a
This has the problem that ReaderT is the outer monad, whilst I want to use it on an inner one.
I've also considered that this might be related to MonadBase or MonadTransControl, but I'm not familiar with their workings.
I don't think it's possible to write a function with signature:
the issue being that the only operation possible, in general, on the second argument is lifting it to
t (m (ReaderT r IO)) a
for some monad transformert
, which doesn't buy you anything.That is, the
MonadTrans m
constraint alone doesn't provide enough structure to do what you want. You either needm
to be an instance of a typeclass likeMFunctor
in themmorph
package that allows you to modify an inner layer of the monad stack in a general way by providing a function like:(which is what @Juan Pablo Santos was saying), or else you need an ability to dig into the structure of your
m
monad transformer to partially run and rebuild it (which will be transformer-specific).The first approach (using
hoist
from themmorph
package) will be most convenient if yourm
is already made up of transformers supported by themmorph
package. For example, the following typechecks, and you don't have to write any instances:You'll need a
hoist
for each layer inM
.The second approach avoids
hoist
and requisiteMFunctor
instances but requires tailoring to your specificM
. For the above type , it looks something like:You basically need to run the monad down to the
ReaderT
layer and then rebuild it back up, treating layers likeStateT
with care. This is exactly what theMFunctor
instances inmmorph
are doing automatically.