Is there a fundamental way to create two monad transformers, if they are of a different type, but their main monad is of the same type?

I can not do to expand the question. But here is a precedent: suppose you have two monad transformers, t and s , which are converted to the same monad m :

 master :: (MonadTrans t, Monad m) => tmab slave :: (MonadTrans t, Monad m) => smab 

And I want to make master and slave so that they can communicate with each other when m primitives were raised by t and s . Signature may be:

 bound :: (MonadTrans t, MonadTrans s, Monad m, Monoid a) => tmab -> smab -> (...) But what is the type of (...) ? 

Use case in a note with sugar:

 master :: Monoid a => a -> tmab master a = do a <- lift . send $ (a,False) -- * here master is passing function param to slave ... -- * do some logic with a b <- lift . send $ (mempty,True) -- * master terminates slave, and get back result slave :: Monoid a => (a -> b) -> smab slave g = do (a,end) <- lift receive case end of True -> get >>= \b -> exit b _ -> (modify (++[ga])) >> slave g 

Update: send and receive are primitives of type m .

I apologize if this example looks far-fetched or too similar to coroutines, the spirit of the question really has nothing to do with it, so please ignore all the similarities. But the main thing is that the monads t and s could not be reasonably composed with each other before, but after both wrapped some basic monad m , they can now be composed and executed as one function. As for the type of function compiled, I'm really not sure, so some direction is appreciated. Now, if this abstraction already exists, and I just don’t know about it, then it would be better.

+6
source share
1 answer

Yes. Combine hoist with hoist package with lift to do this:

 bound :: (MonadTrans t, MonadTrans s, MFunctor t, Monad m) => tm () -> sm () -> t (sm) () bound master slave = do hoist lift master lift slave 

To understand why this works, hoist type:

 hoist :: (MFunctor t) => (forall x . mx -> nx) -> tmr -> tnr 

hoist allows hoist to modify the base monad of any monad transformer that implements MFunctor (which is most of them).

What the code for bound means is that the two monad transformers are consistent with the final target monad, which in this case is t (sm) . The order in which you nested t and s is up to you, so I just assumed that you want t outside.

Then it is just a matter of using different combinations of hoist and lift to get two subcomputations to agree on the final monad stack. The first works as follows:

 master :: tmr hoist lift master :: t (sm) r 

The second works as follows:

 slave :: smr lift slave :: t (sm) r 

Now they both agree that we can arrange them in one do block, and it will just work.

To learn more about how hoist works, I recommend that you check the documentation for the mmorph package, which has a good tutorial below.

+8
source

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


All Articles