Why doesn't MFunctor 'hoist' have a Monad n limit?

I have an accompanying transformer

data Step yma = Done a | Yield y (CoT yma) data CoT yma = CoT (m (Step yma)) 

with Monad instance

 unCoT :: CoT yma -> m (Step yma) unCoT (CoT m) = m instance Monad m => Monad (CoT ym) where return = CoT . return . Done CoT x >>= f = CoT $ do x' <- x case x' of Done a -> unCoT (fa) Yield yx' -> return (Yield y (x' >>= f)) 

If I define an MFunctor class with the constraints of Monad m and Monad n , I can define hoist

 class MFunctor t where hoist :: (Monad n, Monad m) => (forall a. ma -> na) -> tmb -> tnb instance MFunctor (CoT y) where hoist f (CoT m) = CoT $ do step <- fm return (case step of Done x -> Done x Yield ym' -> Yield y (hoist f m')) 

But mmorph hoist has a limitation of Monad m . Can I define my hoist without it, or is it the lack of commonality of MFunctor ?

EDITOR: I designed it, maybe! But my question still remains: are we sure that there is no commonality here?

 instance MFunctor (CoT y) where hoist f (CoT m) = CoT $ f $ do step <- m return (case step of Done x -> Done x Yield ym' -> Yield y (hoist f m')) 
+8
haskell
source share
2 answers

mmorph was developed in the context of the pipes-3.* series pipes-3.* (it used the internal pipes module ), which had the following functions:

 raise :: (Monad m, MFunctor t1, MonadTrans t2) => t1 mr -> t1 (t2 m) r raise = hoist lift 

If you added the Monad n restriction to hoist , you need to add the Monad (t2 m) restriction to raise . I usually try to minimize the restrictions in my libraries, and I could not find any instances of MFunctor that needed a Monad n restriction, so I removed it.

Side note: CoT yma is the same as Producer yma from pipes , which already has an instance of MFunctor .

+8
source share

You can use any type of t for which you can define hoist' :: (Monad m, Monad n) => (forall t. mt -> nt) -> tma -> tna as MFunctor . But you can use the resulting tna if you have a Monad instance on n . We do this by deferring the application of natural transformation. Or a fancy way of saying this is by applying the coyoneda lemma.

 {-# LANGUAGE RankNTypes, GADTs #-} import Control.Monad.Morph -- Slightly weaker than MFunctor due to the monad constraint on n. class MFunctor' t where hoist' :: (Monad m, Monad n) => (forall b. mb -> nb) -> tma -> tna data MCoyoneda tna where MCoyoneda :: Monad m => (forall b. mb -> nb) -> tma -> MCoyoneda tna liftMCoyoneda :: Monad m => tma -> MCoyoneda tma liftMCoyoneda = MCoyoneda id lowerMCoyoneda' :: (MFunctor' t, Monad n) => MCoyoneda tna -> tna lowerMCoyoneda' (MCoyoneda f tma) = hoist' f tma -- The result is actually slightly stronger than 'MFunctor', as we do not need -- a monad for 'm' either. hoistMCoyoneda :: (forall b. mb -> nb) -> MCoyoneda tma -> MCoyoneda tna hoistMCoyoneda f (MCoyoneda trans tma) = MCoyoneda (f . trans) tma instance MFunctor (MCoyoneda t) where hoist = hoistMCoyoneda 

Therefore, I do not think that we are losing generality, because if you cannot implement an instance for MFunctor , then with the Monad restriction on lowerMCoyoneda' nothing is lost.

I found that this works in a similar problem with RVarT .

+1
source share

All Articles