Changing the internal reader in the transformer stack

I am compiling code from several different places and I am trying to figure out 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 this action in the context of another stack that has a different reading 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 reviewed

 withReaderT :: (r' -> r) -> ReaderT rma -> ReaderT r' ma 

This has a problem that ReaderT is an external monad, and I want to use it on the internal one.

I also thought that this could be due to MonadBase or MonadTransControl, but I am not familiar with their work.

+5
source share
1 answer

I don’t think you can write a function with a signature:

 changeReaderT :: (MonadTrans m) => (r -> r') -> m (ReaderT r IO) a -> m (ReaderT r' IO) a 

the problem is that the only possible operation, in the general case, according to the second argument, raises it by t (m (ReaderT r IO)) a for some monadic transformer t that does not buy anything.

That is, the MonadTrans m restriction alone does not provide enough structure to do what you want. You may need m to be an instance of a class of type MFunctor in the mmorph package, which allows you to change the internal level of the monad stack in general, providing a function such as:

 hoist :: Monad m => (forall a. ma -> na) -> tmb -> tnb 

(this is what @Juan Pablo Santos was talking about), or you need the ability to delve into the structure of your monad transformer m to partially start and rebuild it (which will be connected with the transformer).

The first approach (using the hoist from the hoist package) will be most convenient if your m already consists of transformers supported by the mmorph package. For example, the following typechecks, and you do not need to write any instances:

 type M n = MaybeT (StateT String n) action :: M (ReaderT Double IO) a action = undefined f :: Int -> Double f = fromIntegral desired :: M (ReaderT Int IO) a desired = (hoist $ hoist $ withReaderT fromIntegral) action 

For each layer in m you will need a hoist .

The second approach avoids hoist and require instances of MFunctor , but requires adaptation to your specific m . For the above type, it looks something like this:

 desired' :: M (ReaderT Int IO) a desired' = MaybeT $ StateT $ \s -> (withReaderT fromIntegral . flip runStateT s . runMaybeT) action 

You basically need to run monad to the ReaderT level, and then restore it back by carefully processing layers like StateT . This is exactly what MFunctor instances in mmorph automatically.

+4
source

Source: https://habr.com/ru/post/1271573/


All Articles