Suppose we have the following type class.
class MonadTrans t => MonadLower t where
lower :: Monad m => t Identity a -> t m a
As a trivial example, we can implement an instance of MonadLower for MaybeT as follows.
instance MonadLower MaybeT where
lower (MaybeT (Identity maybe)) = MaybeT (return maybe)
However, I can't figure out how to implement an instance of MonadLower for ContT r.
instance MonadLower (ContT r) where
lower (ContT f) = ContT $ \k -> ???
Is it even possible to create an instance of MonadLower t for every MonadTrans t?
If not, which monad transformers (beside ContT r) can't have MonadLower instances?
Edit: Edward Kmett has defined a similar type class called MonadHoist.
class MonadHoist t where
hoist :: (Monad m, Monad n) => (forall a. m a -> n a) -> t m a -> t n a
It turns out that lower = hoist (return . runIdentity).
According to Mauro we can't define a MonadHoist instance for ContT[1].
What Edward is proposing is to have a class of functorial monad transformers (transformers which are endofunctors in the category Mon(C) of monads over a category C and monad morphisms)
Note that some transformers, like the continuation monad transformer, are not functorial.
There is an example program in Learn You a Haskell:
instance Functor ((->) a) where
fmap = (.)
While I have trouble compiling it:
Duplicate instance declarations:
instance Functor ((->) a) -- Defined at partiallyApplied.hs:6:10
instance Functor ((->) r) -- Defined in ‘GHC.Base’
As How do you override Haskell type class instances provided by package code? mention, I should define a new type for the declaration of Functor. I try so but fail:
newtype Ntype a = N ((->) a)
instance Functor ((->) a) where
fmap = (.)
• Expecting one more argument to ‘(->) a’
Expected a type, but ‘(->) a’ has kind ‘* -> *’
• In the type ‘(->) a’
In the definition of data constructor ‘N’
In the newtype declaration for ‘NewType’
How do I make it works?
Suggested by Alex:
newtype Ntype a b = N (a -> b)
instance Functor (Ntype a) where
fmap f (N g) = N (f . g)
Thanks Alex!
I've defined my own monad transformer:
data Config = Config { ... }
data State = State { ... }
newtype FooT m a = FooT {
runFoo :: ReaderT Config (StateT State m) a
} deriving (Functor, Monad, MonadReader Config, MonadState State)
And I've defined a MonadTrans instance for it.
instance MonadTrans FooT where
lift = FooT . lift . lift
Now, I have various monads that I can't just have be derived for me by the compiler. I'll take MonadIO as the example. So I've defined my MonadIO instance as
instance MonadIO m => MonadIO (FooT m) where
liftIO = lift . liftIO
However, I find that I'm doing a lot of lifting, for each Monad. Why could the author of each Monad typeclass (i.e. MonadIO, MonadCatchIO, MonadFoo) not define a general instance in terms of MonadTrans, instead of making me implement an instance for each new MonadTrans I come up with? a la
instance (MonadIO m, MonadTrans t, Monad (t m)) => MonadIO (t m) where
liftIO = lift . liftIO
That requires UndecidableInstances to compile, and I'm not certain that it's correct (in fact, pretty sure it's incorrect), but serves to express my intent for now.
So, is this possible? If not, why not? Will it ever be?
Let's say that I've come up with an alternative to MonadIO, called
MyMonadIO. It's like MonadIO in every way, except for the name:
class Monad m => MyMonadIO m where
myLiftIO :: IO a -> m a
Assuming your FooT type:
newtype FooT m a = FooT
{ runFoo :: ReaderT Config (StateT AppState m) a
} deriving (Functor, Applicative, Monad, MonadReader Config, MonadState AppState)
It's possible to create an instance of MyMonadIO for ReaderT,
StateT, and finally FooT. I've added extra type annotations to make it
easier for the reader to figure out what's going on:
instance MyMonadIO m => MyMonadIO (ReaderT r m) where
myLiftIO :: IO a -> ReaderT r m a
myLiftIO = (lift :: m a -> ReaderT r m a) . (myLiftIO :: IO a -> m a)
instance MyMonadIO m => MyMonadIO (StateT s m) where
myLiftIO :: IO a -> StateT s m a
myLiftIO = (lift :: m a -> StateT s m a) . (myLiftIO :: IO a -> m a)
instance MyMonadIO m => MyMonadIO (FooT m) where
myLiftIO :: IO a -> FooT m a
myLiftIO = (lift :: m a -> FooT m a) . (myLiftIO :: IO a -> m a)
It's also possbile to use GeneralizedNewtypeDeriving to easily derive
MyMonadIO for FooT (assuming there are already instances for ReaderT and
StateT):
newtype FooT m a = FooT
{ runFoo :: ReaderT Config (StateT AppState m) a
} deriving (Functor, Applicative, Monad, MyMonadIO, MonadReader Config, MonadState AppState)
If you look at the body of the myLiftIO function for the ReaderT, StateT,
and FooT instances, they are exactly the same: lift . myLiftIO.
Here's a repeat of the question:
Why could the author of each Monad typeclass (i.e. MonadIO, MonadCatchIO,
MonadFoo) not define a general instance in terms of MonadTrans, instead of
making me implement an instance for each new MonadTrans I come up with?
For MyMonadIO, this general instance would be as follows:
instance (Monad (t n), MyMonadIO n, MonadTrans t) => MyMonadIO (t n) where
myLiftIO :: IO a -> t n a
myLiftIO = (lift :: n a -> t n a) . (myLiftIO :: IO a -> n a)
With this instance defined, you don't need a specific instance for ReaderT,
StateT, or even FooT.
This requires UndecidableInstances. However, the problem with this is not undecidability, but that this instance overlaps some potentially valid instances of MyMonadIO.
For instance, imagine the following datatype:
newtype FreeIO f a = FreeIO (IO (Either a (f (FreeIO f a))))
instance Functor f => Functor (FreeIO f) where
fmap :: (a -> b) -> FreeIO f a -> FreeIO f b
fmap f (FreeIO io) = FreeIO $ do
eitherA <- io
pure $
case eitherA of
Left a -> Left $ f a
Right fFreeIO -> Right $ fmap f <$> fFreeIO
instance Functor f => Applicative (FreeIO f) where
pure :: a -> FreeIO f a
pure a = FreeIO . pure $ Left a
(<*>) :: FreeIO f (a -> b) -> FreeIO f a -> FreeIO f b
(<*>) (FreeIO ioA2b) (FreeIO ioA) = FreeIO $ do
eitherFa2b <- ioA2b
eitherFa <- ioA
pure $
case (eitherFa2b, eitherFa) of
(Left a2b, Left a) -> Left $ a2b a
(Left a2b, Right fFreeIOa) -> Right $ fmap a2b <$> fFreeIOa
(Right fFreeIOa2b, o) -> Right $ (<*> FreeIO (pure o)) <$> fFreeIOa2b
instance Functor f => Monad (FreeIO f) where
(>>=) :: FreeIO f a -> (a -> FreeIO f b) -> FreeIO f b
(>>=) (FreeIO ioA) mA2b = FreeIO $ do
eitherFa <- ioA
case eitherFa of
Left a ->
let (FreeIO ioB) = mA2b a
in ioB
Right fFreeIOa -> pure . Right $ fmap (>>= mA2b) fFreeIOa
You don't necessarily need to understand this FreeIO datatype (especially the Functor, Applicative, and Monad instances). It's enough just to know that this is a valid data type.
(If you're interested, this is just a free monad wrapped around IO.)
It's possible to write a MyMonadIO instance for FreeIO:
instance Functor f => MyMonadIO (FreeIO f) where
myLiftIO :: IO a -> FreeIO f a
myLiftIO ioA = FreeIO (Left <$> ioA)
We can even imagine writing a function using FreeIO:
tryMyLiftIOWithFreeIO :: Functor f => FreeIO f ()
tryMyLiftIOWithFreeIO = myLiftIO $ print "hello"
If you try to compile tryMyLiftIOWithFreeIO with both this instance (MyMonadIO (FreeIO f)) and the bad instance from above, you get the following error:
test-monad-trans.hs:103:25: error:
• Overlapping instances for MyMonadIO (FreeIO f)
arising from a use of ‘myLiftIO’
Matching instances:
instance (Monad (t n), MyMonadIO n, MonadTrans t) => MyMonadIO (t n)
-- Defined at test-monad-trans.hs:52:10
instance Functor f => MyMonadIO (FreeIO f)
-- Defined at test-monad-trans.hs:98:10
• In the expression: myLiftIO $ print "hello"
In an equation for ‘tryMyLiftIOWithFreeIO’:
tryMyLiftIOWithFreeIO = myLiftIO $ print "hello"
Why does this happen?
Well, in instance (Monad (t n), MyMonadIO n, MonadTrans t) => MyMonadIO (t n), what is the kind of t and n?
Since n is supposed to be a Monad, it's kind is * -> *. And since t is a monad transformer, it's kind is (* -> *) -> * -> *. t n is also supposed to be a Monad, so it's kind is also * -> *:
n :: * -> *
t :: (* -> *) -> * -> *
t n :: * -> *
Now, in instance Functor f => MyMonadIO (FreeIO f), what are the kinds of FreeIO and f?
f is supposed to be a Functor, so it's kind is * -> *. FreeIO's kind is (* -> *) -> * -> *. FreeIO f is a Monad, so it's kind is * -> *:
f :: * -> *
FreeIO :: (* -> *) -> * -> *
FreeIO f :: * -> *
Since the kinds are the same, you an see that instance Functor f => MyMonadIO (FreeIO f) overlaps with instance (Monad (t n), MyMonadIO n, MonadTrans t) => MyMonadIO (t n). GHC isn't sure which one to pick!
You can get around this by marking your instance FreeIO instance as OVERLAPPING:
instance {-# OVERLAPPING #-} Functor f => MyMonadIO (FreeIO f) where
myLiftIO :: IO a -> FreeIO f a
myLiftIO m = FreeIO (Left <$> m)
However, this is a treacherous route to go down. You can find out more about why overlapping can be bad from the GHC user guide.
This FreeIO example was created by Edward Kmett. You can find another clever example of an overlapping instance in this reddit post.
If you are planning on writing a monad typeclass (like MyMonadIO) and
releasing it to Hackage, one option is to use the
DefaultSignatures
functionality. This makes it easier for users of your library to define
instances.
Using DefaultSignatures, defining the MyMonadIO class would look like this:
class Monad m => MyMonadIO m where
myLiftIO :: IO a -> m a
default myLiftIO
:: forall t n a.
( MyMonadIO n
, MonadTrans t
, m ~ t n
)
=> IO a -> t n a
myLiftIO = (lift :: n a -> t n a) . (myLiftIO :: IO a -> n a)
This says that there is a default implementation of myLiftIO for any t n,
where n is an instance of MyMonadIO, and t is an instance of
MonadTrans.
With this default siguature for myLiftIO, defining instances of MyMonadIO for ReaderT and StateT would look like this:
instance MyMonadIO m => MyMonadIO (ReaderT r m)
instance MyMonadIO m => MyMonadIO (StateT s m)
Very simple. You don't need to provide the function body of myLiftIO since
it will use the default.
The only drawback of this is that it is not widely done. The
DefaultSignatures machinery seems to be mainly used for generic
programming, not monad typeclasses.
I have some function that composes two monads:
comp :: Monad m => m a -> m b -> m b
And two instances of such monads, where on is "inside" an Mfunctor,
ms :: Monad m => m String
ms = undefined
tma :: (Monad m, MFunctor t) => t m a
tma = undefined
Now if I try to compose ms with tma:
tmas = hoist (\ma -> comp ma ms) tma
I am getting this error:
Could not deduce (a ~ [Char])
from the context (Monad m, MFunctor t)
bound by the inferred type of
comp :: (Monad m, MFunctor t) => t m b
at Coroutine.hs:607:1-40
`a' is a rigid type variable bound by
a type expected by the context: m a -> m a at Coroutine.hs:607:8
Expected type: m a
Actual type: m String
which states that a in ms has to be of arbitrary type: ms :: Monad m => m a.
Why is this and is there a way to compose tma with monads of specific parameters.
I can see that signature of hoist is:
hoist :: (Monad m, MFunctor t) => (forall a. m a -> n a) -> t m b -> t n b
but cannot picture how forall is affecting what I am trying to do, if it has any effect.
Switch the order of arguments to comp like this:
tmas = hoist (\ma -> comp ms ma) tma
-- or more simply:
tmas = hoist (comp ms) tma
The reason is that the type of comp is:
comp :: (Monad m) => m a -> m b -> m b
If you set, ms as the second argument, the b type-checks as a String and you get:
(`comp` ms) :: (Monad m) => m a -> m String
... but if you set ms as the first argument, the a type-checks as a String you get:
(ms `comp`) :: (Monad m) => m b -> m b
That latter type is the correct type for hoist since the b is universally quantified (i.e. "forall"ed).
To answer your question about correctness, the answer is that the universal quantification guarantees that the argument to hoist only modifies the monad layer and not the monad return value. However, if it's your intention to modify the return value, too, then hoist is not what you want.
The type of hoist says that it expects a function (forall a. m a -> n a) i.e. a function that changes the "container" type but keeps the type parameter the same. The forall here means that the function you provide cannot be specialized for any specific a but has to work for any type parameter.
The function you are trying to use (\ma -> comp ma ms) has the type m a -> m String so it is pretty much the opposite of what hoist expects since it keeps the container (m) the same but changes the type parameter (from a to String).
I think what you are actually looking for instead of hoist in this case is a function that lifts a monadic function to work on transformed monads, so instead of MFunctor you'll want something like:
import Control.Monad.Trans.Class
tmas :: (Monad m, Monad (t m), MonadTrans t) => t m String
tmas = transLift (\ma -> comp ma ms) tma
transLift :: (Monad m, Monad (t m), MonadTrans t) => (m a -> m b) -> t m a -> t m b
transLift f tma = tma >>= lift . f . return
A lot of constraints seem to come together. Let's abstract these away.
type MonadNumState a m = (MonadState a m, Num a)
MonadNumState is just a constraint synonym, so I get the benefit of functional dependencies at every use, and can easily throw a MonadNumState a m into a context. Now, suppose I wish to abstract this into a constraint family:
class Iterator t where
type MonadIter t a m :: Constraint
next :: (MonadIter t a m) => m t
...
instance Iterator Foo where
type MonadIter Foo a m = (MonadState a m, Num a)
...
instance Iterator Bar where
type MonadIter Bar a m = (MonadRandom m, MonadSplit a m, RandomGen a)
...
But now a is not a functional dependency. next is virtually unusable since a cannot be inferred. What can I do? Well, I could, of course, use a type family instead. MonadState is written using fundeps, but it should be easy to convert the fundeps to type families.
instance (MonadState s m) => MonadStateFamily m where
type St m = s
get' = get
...
instance (MonadStateFamily m) => MonadState (St m) m where
get = get'
...
Guess not.
Foo.hs:25:3:
The RHS of an associated type declaration mentions type variable `s'
All such variables must be bound on the LHS
What else might I be able to do? What I really want is to existentially quantify away s. I've not found any way to do that without explicit dictionary passing.
So, how do I get the benefit of fundeps for constraint families?
You can consider using a standalone type family instead of an associated type
type family StateType (m:: * -> *)
Then you can define
class MonadStateFamily m where
get' :: m (StateType m)
instance (MonadState s m, s ~ StateType m) => MonadStateFamily m where
get' = get
and use it on a concrete monad like this:
type instance StateType (State s) = s
getState :: State s s
getState = get'