Open juliapath opened 7 years ago
The only suggestion I'd make is to not add the MFunctor'
class (mainly because I'd like to avoid type class proliferation) and instead replace lowerMCoyoneda
with another function which I'm calling withCoyoneda
:
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ExistentialQuantification #-}
import Control.Monad.Morph
-- Minor simplification to use just `ExistentialQuantification`
data MCoyoneda t n a =
forall m . Monad m => MCoyoneda (forall b . m b -> n b) (t m a)
liftMCoyoneda :: Monad m => t m a -> MCoyoneda t m a
liftMCoyoneda = MCoyoneda id
withCoyoneda
:: (forall m . Monad m => (forall b . m b -> n b) -> t m a -> r)
-> MCoyoneda t n a
-> r
withCoyoneda k (MCoyoneda f tma) = k f tma
instance MFunctor (MCoyoneda t) where
hoist f (MCoyoneda trans tma) = MCoyoneda (f . trans) tma
Then you can choose what hoist
function to supply to withCoyoneda
. For example, if you supply withCoyoneda
with hoist
, then you get this inferred type:
>>> :type withCoyoneda hoist
withCoyoneda hoist :: MFunctor t => MCoyoneda t n a -> t n a
... but if you supply withCoyoneda
with hoist'
then you get this inferred type:
>>> :type withCoyoneda hoist'
withCoyoneda hoist'
:: (MFunctor' t, Monad n) => MCoyoneda t n a -> t n a
That then would let you keep MCoyoneda
in mmorph
but you could define MFunctor'
separately and supply your hoist'
function to withCoyoneda
Sometimes it can be difficult to implement an
MFunctor
instance for a given type due to the lack of aMonad
constraint on the result type ofhoist
. Especially if you only have access to what is exported in the API for that type (e.g. 1, 2). However this can be worked around using a coyoneda construction like this (from my answer in the second link):Would this be a useful addition to
mmorph
?