Related
There have been a couple of questions (e.g. this and this) asking whether every monad in Haskell (other than IO) has a corresponding monad transformer. Now I would like to ask a complementary question. Does every monad have exactly one transformer (or none as in the case of IO) or can it have more than one transformer?
A counterexample would be two monad transformers that would produce monads behaving identically when applied to the identity monad would but would produce differently behaving monads when applied to some other monad. If the answer is that a monad can have more than one transformer I would like to have a Haskell example which is as simple as possible. These don't have to be actually useful transformers (though that would be interesting).
Some of the answers in the linked question seemed to suggest that a monad could have more than one transformer. However, I don't know much category theory beyond the basic definition of a category so I wasn't sure whether they are an answer to this question.
Here's one idea for a counterexample to uniqueness. We know that in general, monads don't compose... but we also know that if there's an appropriate swap operation, you can compose them[1]! Let's make a class for monads that can swap with themselves.
-- | laws (from [1]):
-- swap . fmap (fmap f) = fmap (fmap f) . swap
-- swap . pure = fmap pure
-- swap . fmap pure = pure
-- fmap join . swap . fmap (join . fmap swap) = join . fmap swap . fmap join . swap
class Monad m => Swap m where
swap :: m (m a) -> m (m a)
instance Swap Identity where swap = id
instance Swap Maybe where
swap Nothing = Just Nothing
swap (Just Nothing) = Nothing
swap (Just (Just x)) = Just (Just x)
Then we can build a monad transformer that composes a monad with itself, like so:
newtype Twice m a = Twice (m (m a))
Hopefully it should be obvious what pure and (<$>) do. Rather than defining (>>=), I'll define join, as I think it's a bit more obvious what's going on; (>>=) can be derived from it.
instance Swap m => Monad (Twice m) where
join = id
. Twice -- rewrap newtype
. fmap join . join . fmap swap -- from [1]
. runTwice . fmap runTwice -- unwrap newtype
instance MonadTrans Twice where lift = Twice . pure
I haven't checked that lift obeys the MonadTrans laws for all Swap instances, but I did check them for Identity and Maybe.
Now, we have
IdentityT Identity ~= Identity ~= Twice Identity
IdentityT Maybe ~= Maybe !~= Twice Maybe
which shows that IdentityT is not a unique monad transformer for producing Identity.
[1] Composing monads by Mark P. Jones and Luc Duponcheel
The identity monad has at least two monad transformers: the identity monad transformer and the codensity monad transformer.
newtype IdentityT m a = IdentityT (m a)
newtype Codensity m a = Codensity (forall r. (a -> m r) -> m r)
Indeed, considering Codensity Identity, forall r. (a -> r) -> r is isomorphic to a.
These monad transformers are quite different. One example is that "bracket" can be defined as a monadic action in Codensity:
bracket :: Monad m => m () -> m () -> Codensity m ()
bracket before after = Codensity (\k -> before *> k () *> after)
whereas transposing that signature to IdentityT doesn't make much sense
bracket :: Monad m => m () -> m () -> IdentityT m () -- cannot implement the same functionality
Other examples can be found similarly from variants of the continuation/codensity monad, though I don't see a general scheme yet.
The state monad corresponds to the state monad transformer and to the composition of Codensity and ReaderT:
newtype StateT s m a = StateT (s -> m (s, a))
newtype CStateT s m a = CStateT (Codensity (ReaderT s m) a)
The list monad corresponds to at least three monad transformers, not including the wrong one:
newtype ListT m a = ListT (m (Maybe (a, ListT m a))) -- list-t
newtype LogicT m a = LogicT (forall r. (a -> m r -> m r) -> m r -> m r) -- logict
newtype MContT m a = MContT (forall r. Monoid r => (a -> m r) -> m r))
The first two can be found respectively in the packages list-t (also in an equivalent form in pipes),
and logict.
There is another example of a monad that has two inequivalent transformers: the "selection" monad.
type Sel r a = (a -> r) -> a
The monad is not well known but there are papers that mention it. Here is a package that refers to some papers:
https://hackage.haskell.org/package/transformers-0.6.0.4/docs/Control-Monad-Trans-Select.html
That package implements one transformer:
type SelT r m a = (a -> m r) -> m a
But there exists a second transformer:
type Sel2T r m a = (m a -> r ) -> m a
Proving laws for this transformer is more difficult but I have done it.
An advantage of the second transformer is that it is covariant in m, so the hoist function can be defined:
hoist :: (m a -> n a) -> Sel2T r m a -> Sel2T r n a
The second transformer is "fully featured", has all lifts and "hoist". The first transformer is not fully featured; for example, you cannot define blift :: Sel r a -> SelT r m a. In other words, you cannot embed monadic computations from Sel into SelT, just like you can't do that with the Continuation monad and the Codensity monad.
But with the Sel2T transformer, all lifts exist and you can embed Sel computations into Sel2T.
This example shows a monad with two transformers without using the Codensity construction in any way.
Applicatives compose, monads don't.
What does the above statement mean? And when is one preferable to other?
If we compare the types
(<*>) :: Applicative a => a (s -> t) -> a s -> a t
(>>=) :: Monad m => m s -> (s -> m t) -> m t
we get a clue to what separates the two concepts. That (s -> m t) in the type of (>>=) shows that a value in s can determine the behaviour of a computation in m t. Monads allow interference between the value and computation layers. The (<*>) operator allows no such interference: the function and argument computations don't depend on values. This really bites. Compare
miffy :: Monad m => m Bool -> m x -> m x -> m x
miffy mb mt mf = do
b <- mb
if b then mt else mf
which uses the result of some effect to decide between two computations (e.g. launching missiles and signing an armistice), whereas
iffy :: Applicative a => a Bool -> a x -> a x -> a x
iffy ab at af = pure cond <*> ab <*> at <*> af where
cond b t f = if b then t else f
which uses the value of ab to choose between the values of two computations at and af, having carried out both, perhaps to tragic effect.
The monadic version relies essentially on the extra power of (>>=) to choose a computation from a value, and that can be important. However, supporting that power makes monads hard to compose. If we try to build ‘double-bind’
(>>>>==) :: (Monad m, Monad n) => m (n s) -> (s -> m (n t)) -> m (n t)
mns >>>>== f = mns >>-{-m-} \ ns -> let nmnt = ns >>= (return . f) in ???
we get this far, but now our layers are all jumbled up. We have an n (m (n t)), so we need to get rid of the outer n. As Alexandre C says, we can do that if we have a suitable
swap :: n (m t) -> m (n t)
to permute the n inwards and join it to the other n.
The weaker ‘double-apply’ is much easier to define
(<<**>>) :: (Applicative a, Applicative b) => a (b (s -> t)) -> a (b s) -> a (b t)
abf <<**>> abs = pure (<*>) <*> abf <*> abs
because there is no interference between the layers.
Correspondingly, it's good to recognize when you really need the extra power of Monads, and when you can get away with the rigid computation structure that Applicative supports.
Note, by the way, that although composing monads is difficult, it might be more than you need. The type m (n v) indicates computing with m-effects, then computing with n-effects to a v-value, where the m-effects finish before the n-effects start (hence the need for swap). If you just want to interleave m-effects with n-effects, then composition is perhaps too much to ask!
Applicatives compose, monads don't.
Monads do compose, but the result might not be a monad.
In contrast, the composition of two applicatives is necessarily an applicative.
I suspect the intention of the original statement was that "Applicativeness composes, while monadness doesn't." Rephrased, "Applicative is closed under composition, and Monad is not."
If you have applicatives A1 and A2, then the type data A3 a = A3 (A1 (A2 a)) is also applicative (you can write such an instance in a generic way).
On the other hand, if you have monads M1 and M2 then the type data M3 a = M3 (M1 (M2 a)) is not necessarily a monad (there is no sensible generic implementation for >>= or join for the composition).
One example can be the type [Int -> a] (here we compose a type constructor [] with (->) Int, both of which are monads). You can easily write
app :: [Int -> (a -> b)] -> [Int -> a] -> [Int -> b]
app f x = (<*>) <$> f <*> x
And that generalizes to any applicative:
app :: (Applicative f, Applicative f1) => f (f1 (a -> b)) -> f (f1 a) -> f (f1 b)
But there is no sensible definition of
join :: [Int -> [Int -> a]] -> [Int -> a]
If you're unconvinced of this, consider this expression:
join [\x -> replicate x (const ())]
The length of the returned list must be set in stone before an integer is ever provided, but the correct length of it depends on the integer that's provided. Thus, no correct join function can exist for this type.
Unfortunately, our real goal, composition of monads, is rather more
difficult. .. In fact, we
can actually prove that, in a certain sense, there is no way to
construct a join function with the type above using only the
operations of the two monads (see the appendix for an outline of the
proof). It follows that the only way that we might hope to form a
composition is if there are some additional constructions linking the
two components.
Composing monads, http://web.cecs.pdx.edu/~mpj/pubs/RR-1004.pdf
The distributive law solution
l : MN -> NM is enough
to guarantee monadicity of NM. To see this you need a unit and a mult. i'll focus on the mult (the unit is unit_N unitM)
NMNM - l -> NNMM - mult_N mult_M -> NM
This does not guarantee that MN is a monad.
The crucial observation however, comes into play when you have distributive law solutions
l1 : ML -> LM
l2 : NL -> LN
l3 : NM -> MN
thus, LM, LN and MN are monads. The question arises as to whether LMN is a monad (either by
(MN)L -> L(MN)
or by
N(LM) -> (LM)N
We have enough structure to make these maps. However, as Eugenia Cheng observes, we need a hexagonal condition (that amounts to a presentation of the Yang-Baxter equation) to guarantee monadicity of either construction. In fact, with the hexagonal condition, the two different monads coincide.
Any two applicative functors can be composed and yield another applicative functor. But this does not work with monads. A composition of two monads is not always a monad. For example, a composition of State and List monads (in any order) is not a monad.
Moreover, one cannot combine two monads in general, whether by composition or by any other method. There is no known algorithm or procedure that combines any two monads M, N into a larger, lawful monad T so that you can inject M ~> T and N ~> T by monad morphisms and satisfy reasonable non-degeneracy laws (e.g., to guarantee that T is not just a unit type that discards all effects from M and N).
It is possible to define a suitable T for specific M and N, for example of M = Maybe and N = State s and so on. But it is unknown how to define T that would work parametrically in the monads M and N. Neither functor composition, nor more complicated constructions work adequately.
One way of combining monads M and N is first, to define the co-product C a = Either (M a) (N a). This C will be a functor but, in general, not a monad. Then one constructs a free monad (Free C) on the functor C. The result is a monad that is able to represent effects of M and N combined. However, it is a much larger monad that can also represent other effects; it is much larger than just a combination of effects of M and N. Also, the free monad will need to be "run" or "interpreted" in order to extract any results (and the monad laws are guaranteed only after "running"). There will be a run-time penalty as well as memory size penalty because the free monad will potentially build very large structures in memory before it is "run". If these drawbacks are not significant, the free monad is the way to go.
Another way of combining monads is to take one monad's transformer and apply it to the other monad. But there is no algorithmic way of taking a definition of a monad (e.g., type and code in Haskell) and producing the type and code of the corresponding transformer.
There are at least 4 different classes of monads whose transformers are constructed in completely different but regular ways (composed-inside, composed-outside, adjunction-based monad, product monad). A few other monads do not belong to any of these "regular" classes and have transformers defined "ad hoc" in some way.
Distributive laws exist only for composed monads. It is misleading to think that any two monads M, N for which one can define some function M (N a) -> N (M a) will compose. In addition to defining a function with this type signature, one needs to prove that certain laws hold. In many cases, these laws do not hold.
There are even some monads that have two inequivalent transformers; one defined in a "regular" way and one "ad hoc". A simple example is the identity monad Id a = a; it has the regular transformer IdT m = m ("composed") and the irregular "ad hoc" one: IdT2 m a = forall r. (a -> m r) -> m r (the codensity monad on m).
A more complicated example is the "selector monad": Sel q a = (a -> q) -> a. Here q is a fixed type and a is the main type parameter of the monad Sel q. This monad has two transformers: SelT1 m a = (m a -> q) -> m a (composed-inside) and SelT2 m a = (a -> m q) -> m a (ad hoc).
Full details are worked out in Chapter 14 of the book "The Science of Functional Programming". https://github.com/winitzki/sofp or https://leanpub.com/sofp/
Here is some code making monad composition via a distributive law work. Note that there are distributive laws from any monad to the monads Maybe, Either, Writer and []. On the other hand, you won't find such (general) distributive laws into Reader and State. For these, you will need monad transformers.
{-# LANGUAGE FlexibleInstances #-}
module ComposeMonads where
import Control.Monad
import Control.Monad.Writer.Lazy
newtype Compose m1 m2 a = Compose { run :: m1 (m2 a) }
instance (Functor f1, Functor f2) => Functor (Compose f1 f2) where
fmap f = Compose . fmap (fmap f) . run
class (Monad m1, Monad m2) => DistributiveLaw m1 m2 where
dist :: m2 (m1 a) -> m1 (m2 a)
instance (Monad m1,Monad m2, DistributiveLaw m1 m2)
=> Applicative (Compose m1 m2) where
pure = return
(<*>) = ap
instance (Monad m1, Monad m2, DistributiveLaw m1 m2)
=> Monad (Compose m1 m2) where
return = Compose . return . return
Compose m1m2a >>= g =
Compose $ do m2a <- m1m2a -- in monad m1
m2m2b <- dist $ do a <- m2a -- in monad m2
let Compose m1m2b = g a
return m1m2b
-- do ... :: m2 (m1 (m2 b))
-- dist ... :: m1 (m2 (m2 b))
return $ join m2m2b -- in monad m2
instance Monad m => DistributiveLaw m Maybe where
dist Nothing = return Nothing
dist (Just m) = fmap Just m
instance Monad m => DistributiveLaw m (Either s) where
dist (Left s) = return $ Left s
dist (Right m) = fmap Right m
instance Monad m => DistributiveLaw m [] where
dist = sequence
instance (Monad m, Monoid w) => DistributiveLaw m (Writer w) where
dist m = let (m1,w) = runWriter m
in do a <- m1
return $ writer (a,w)
liftOuter :: (Monad m1, Monad m2, DistributiveLaw m1 m2) =>
m1 a -> Compose m1 m2 a
liftOuter = Compose . fmap return
liftInner :: (Monad m1, Monad m2, DistributiveLaw m1 m2) =>
m2 a -> Compose m1 m2 a
liftInner = Compose . return
I have a Monad of named TaskMonad, defined as follows:
data TaskMonad a = TaskMonad (Environment -> (TaskResult a, Environment))
where Environment is a record type and TaskResult is an ADT; but they are not important for the problem.
I have defed Functor, Applicative and Monad instances for TaskMonad, and I now want to be able to combine this monad with other Monads (e.g. IO), So I defined a new type as follows:
newtype Task m a = Task { runTask :: m (TaskMonad a) }
I have defined Functor and Applicative as follows:
instance Monad m => Functor (Task m) where
fmap f ta = Task $ do tma <- runTask ta
return (fmap f tma)
instance Monad m => Applicative (Task m) where
pure = Task . return . return
(<*>) prod tx = Task $ do tmprod <- runTask prod
tmtx <- runTask tx
return (tmprod <*> tmtx)
And I also made Task member of the MonadTrans class:
instance MonadTrans Task where
lift = Task . (liftM return)
So far so good (or atleast it compiles..), but now I want to define the instance for Monad, but I am running into problems here:
instance Monad m => Monad (Task m) where
return = pure
(>>=) ta tb = ...
I attempted multiple things, most attempts starting out like this:
(>>=) ta tb = Task $ do tma <- runTask ta
Now we have tma :: TaskMonad a inside the do block for the m monad. Now what I would like to do, is somehow calling the >>= instance for TaskMonad so I can get the result of tma, a value of type a so I can parameterize tb with it to obtain a value of Task b. But I am within the context of the m monad and I'm running into all kinds of problems.
How could I obtain tma's result to provide it to tb?
Okay, I don't know how much this helps, but if you actually start with a transformer from day 0 (in TaskMonad), the way you can do it is:
data TaskMonad m a = TaskMonad (Environment -> m (TaskResult a, Environment)) deriving Functor
instance Monad m => Monad (TaskMonad m) where
return = pure
(TaskMonad f) >>= b = TaskMonad $ \e -> do
(TaskResult r, e') <- f e
let (TaskMonad g) = b r
g e'
instance (Monad m, Functor m) => Applicative (TaskMonad m) where
pure a = TaskMonad $ \e -> return (TaskResult a, e)
(TaskMonad f) <*> (TaskMonad g) = TaskMonad $ \e -> do
(TaskResult f', e') <- f e
(TaskResult a, e'') <- g e'
return (TaskResult (f' a), e'')
Probably there's also a way to do that the way you originally intended, but I am pretty sure original Task would also need to be changed to take initial Environment.
I presume you're actually doing more than State in your monad, so that would need to be put in respective instances, but I think the framework for that should help.
And of course, shall you ever need to use a non-transformer version, just pass in Identity for m.
Disclaimer:
I know this implemenation of Applicative instance doesn't make sense, but I was building that on old GHC w/o ApplicativeDo and it was literally the easiest thing to put the silly constraint there.
As described in #BartekBanachewicz's answer, putting the monad m inside -> is the way to go.
I believe it's not possible to do it the way you want by having m (TaskMonad a), at least not generically. In general monads aren't closed under composition and this is an example of such a situation.
Let me give a simplified example (some theory will be required for it): Let's work with the reader monad instead of the state monad, let's drop TaskResult and let's have the environment as a type parameter. So TaskMonad will be just m (r -> a). Now let's assume it's a monad, then there is
join :: m (r -> (m (r -> a))) -> m (r -> a)
Specializing a to Void (see also Bottom type) and m to Either r we get
join :: Either r (r -> (Either r (r -> Void))) -> Either r (r -> Void)
But then we're able to construct
doubleNegationElimination :: Either r (r -> Void)
doubleNegationElimination = join (Right Left)
as Right Left :: Either r (r -> Either r (r -> Void)). Through Curry-Howard isomorphism this would mean that we'd be able to prove Double negation elimination
in intuitionistic logic, which is a contradiction.
Your situation is somewhat more complex, but a similar argument could be made there too. The only hole there is that we assumed that the "environment" part, r, was generic, so won't work if your join or >>= is somehow specific for Environment. So you might be able to do it in such a case, but my guess is you'll then encounter other problems preventing you to get a proper non-trivial Monad instance.
Imagine that I have a value that is generic over the monad:
m :: (Monad m) => m A -- 'A' is some concrete type
Now let's say that I specialize this value to a concrete monad transformer stack in two separate ways:
m1 :: T M A
m1 = m
m2 :: T M A
m2 = lift m
... where M and T M are monads, and T is a monad transformer:
instance Monad M where ...
instance (Monad m) => Monad (T m) where ...
instance MonadTrans T where ...
... and those instances obey the monad laws and monad transformer laws.
Can we deduce that:
m1 = m2
... knowing nothing about m other than its type?
This is just a long-winded way of asking if lift m is a valid substitution for m, assuming that both type-check. It is a little bit difficult to phrase the question because it requires m type-checking as two separate monads before and after the substitution. As far as I can tell, the only way such a substitution would type-check is if m is generic over the monad.
My vague intuition is that the substitution should always be correct, but I'm not sure that my intuition is correct, or how to prove it if it is correct.
If m :: Monad m => m A, then m must be equivalent to return x for some x :: A, because the only ways you have to get anything :: m x are return and (>>=). But in order to use (>>=) you must be able to produce some m y, which you can either do with return or with another application of (>>=). Either way you have to use return eventually, and the monad laws guarantee that the whole expression will be equivalent to return x.
(If m is completely polymorphic over the monad, then you must able to use it at m ~ Identity, so it can't use any fancy monad tricks, unless you pass it an argument. This sort of trick is used e.g. here and here.)
Given that m = return x, we know by the monad transformer laws (lift . return = return) that lift m = m.
Of course, this is only true for this particular type. If you have, say, m :: MonadState S m => m A, then m could easily be different from lift m -- for example, with a type like StateT A (State A) A, get and lift get will be different.
(And of course all of this is ignoring ⊥. Then again, if you don't, most monads don't obey the laws anyway.)
I believe this is a sloppy inductive proof that your m is equivalent to lift m.
I think we have to try to prove something about m (or rather, about all possible values of type (Monad m) => m A). If we consider Monad as consisting of bind and return only, and ignore bottom and fail then your m must at the top level be one of:
mA = return (x)
mB = (mX >>= f)
For mA the two forms of m are equivalent by the monad transformer law:
lift (return (x)) = return (x)
That's the base case. Then we're left with the second transformer law to reason about mB:
lift (mX >>= f) = lift mX >>= (lift . f)
and where we'd like to prove that that our mB is equal to that expansion:
mX >>= f = lift mX >>= (lift . f)
we assume that the left side of bind is equivalent (mX = lift mX) since that's our inductive hypothesis (right?).
So then we're left to prove f = lift . f by figuring out what f has to look like:
f :: a -> m b
f = \a -> (one of our forms mA or mB)
and lift . f looks like:
f = \a -> lift (one of our forms mA or mB)
Which leaves us back with our hypothesis:
m = lift m
Applicatives compose, monads don't.
What does the above statement mean? And when is one preferable to other?
If we compare the types
(<*>) :: Applicative a => a (s -> t) -> a s -> a t
(>>=) :: Monad m => m s -> (s -> m t) -> m t
we get a clue to what separates the two concepts. That (s -> m t) in the type of (>>=) shows that a value in s can determine the behaviour of a computation in m t. Monads allow interference between the value and computation layers. The (<*>) operator allows no such interference: the function and argument computations don't depend on values. This really bites. Compare
miffy :: Monad m => m Bool -> m x -> m x -> m x
miffy mb mt mf = do
b <- mb
if b then mt else mf
which uses the result of some effect to decide between two computations (e.g. launching missiles and signing an armistice), whereas
iffy :: Applicative a => a Bool -> a x -> a x -> a x
iffy ab at af = pure cond <*> ab <*> at <*> af where
cond b t f = if b then t else f
which uses the value of ab to choose between the values of two computations at and af, having carried out both, perhaps to tragic effect.
The monadic version relies essentially on the extra power of (>>=) to choose a computation from a value, and that can be important. However, supporting that power makes monads hard to compose. If we try to build ‘double-bind’
(>>>>==) :: (Monad m, Monad n) => m (n s) -> (s -> m (n t)) -> m (n t)
mns >>>>== f = mns >>-{-m-} \ ns -> let nmnt = ns >>= (return . f) in ???
we get this far, but now our layers are all jumbled up. We have an n (m (n t)), so we need to get rid of the outer n. As Alexandre C says, we can do that if we have a suitable
swap :: n (m t) -> m (n t)
to permute the n inwards and join it to the other n.
The weaker ‘double-apply’ is much easier to define
(<<**>>) :: (Applicative a, Applicative b) => a (b (s -> t)) -> a (b s) -> a (b t)
abf <<**>> abs = pure (<*>) <*> abf <*> abs
because there is no interference between the layers.
Correspondingly, it's good to recognize when you really need the extra power of Monads, and when you can get away with the rigid computation structure that Applicative supports.
Note, by the way, that although composing monads is difficult, it might be more than you need. The type m (n v) indicates computing with m-effects, then computing with n-effects to a v-value, where the m-effects finish before the n-effects start (hence the need for swap). If you just want to interleave m-effects with n-effects, then composition is perhaps too much to ask!
Applicatives compose, monads don't.
Monads do compose, but the result might not be a monad.
In contrast, the composition of two applicatives is necessarily an applicative.
I suspect the intention of the original statement was that "Applicativeness composes, while monadness doesn't." Rephrased, "Applicative is closed under composition, and Monad is not."
If you have applicatives A1 and A2, then the type data A3 a = A3 (A1 (A2 a)) is also applicative (you can write such an instance in a generic way).
On the other hand, if you have monads M1 and M2 then the type data M3 a = M3 (M1 (M2 a)) is not necessarily a monad (there is no sensible generic implementation for >>= or join for the composition).
One example can be the type [Int -> a] (here we compose a type constructor [] with (->) Int, both of which are monads). You can easily write
app :: [Int -> (a -> b)] -> [Int -> a] -> [Int -> b]
app f x = (<*>) <$> f <*> x
And that generalizes to any applicative:
app :: (Applicative f, Applicative f1) => f (f1 (a -> b)) -> f (f1 a) -> f (f1 b)
But there is no sensible definition of
join :: [Int -> [Int -> a]] -> [Int -> a]
If you're unconvinced of this, consider this expression:
join [\x -> replicate x (const ())]
The length of the returned list must be set in stone before an integer is ever provided, but the correct length of it depends on the integer that's provided. Thus, no correct join function can exist for this type.
Unfortunately, our real goal, composition of monads, is rather more
difficult. .. In fact, we
can actually prove that, in a certain sense, there is no way to
construct a join function with the type above using only the
operations of the two monads (see the appendix for an outline of the
proof). It follows that the only way that we might hope to form a
composition is if there are some additional constructions linking the
two components.
Composing monads, http://web.cecs.pdx.edu/~mpj/pubs/RR-1004.pdf
The distributive law solution
l : MN -> NM is enough
to guarantee monadicity of NM. To see this you need a unit and a mult. i'll focus on the mult (the unit is unit_N unitM)
NMNM - l -> NNMM - mult_N mult_M -> NM
This does not guarantee that MN is a monad.
The crucial observation however, comes into play when you have distributive law solutions
l1 : ML -> LM
l2 : NL -> LN
l3 : NM -> MN
thus, LM, LN and MN are monads. The question arises as to whether LMN is a monad (either by
(MN)L -> L(MN)
or by
N(LM) -> (LM)N
We have enough structure to make these maps. However, as Eugenia Cheng observes, we need a hexagonal condition (that amounts to a presentation of the Yang-Baxter equation) to guarantee monadicity of either construction. In fact, with the hexagonal condition, the two different monads coincide.
Any two applicative functors can be composed and yield another applicative functor. But this does not work with monads. A composition of two monads is not always a monad. For example, a composition of State and List monads (in any order) is not a monad.
Moreover, one cannot combine two monads in general, whether by composition or by any other method. There is no known algorithm or procedure that combines any two monads M, N into a larger, lawful monad T so that you can inject M ~> T and N ~> T by monad morphisms and satisfy reasonable non-degeneracy laws (e.g., to guarantee that T is not just a unit type that discards all effects from M and N).
It is possible to define a suitable T for specific M and N, for example of M = Maybe and N = State s and so on. But it is unknown how to define T that would work parametrically in the monads M and N. Neither functor composition, nor more complicated constructions work adequately.
One way of combining monads M and N is first, to define the co-product C a = Either (M a) (N a). This C will be a functor but, in general, not a monad. Then one constructs a free monad (Free C) on the functor C. The result is a monad that is able to represent effects of M and N combined. However, it is a much larger monad that can also represent other effects; it is much larger than just a combination of effects of M and N. Also, the free monad will need to be "run" or "interpreted" in order to extract any results (and the monad laws are guaranteed only after "running"). There will be a run-time penalty as well as memory size penalty because the free monad will potentially build very large structures in memory before it is "run". If these drawbacks are not significant, the free monad is the way to go.
Another way of combining monads is to take one monad's transformer and apply it to the other monad. But there is no algorithmic way of taking a definition of a monad (e.g., type and code in Haskell) and producing the type and code of the corresponding transformer.
There are at least 4 different classes of monads whose transformers are constructed in completely different but regular ways (composed-inside, composed-outside, adjunction-based monad, product monad). A few other monads do not belong to any of these "regular" classes and have transformers defined "ad hoc" in some way.
Distributive laws exist only for composed monads. It is misleading to think that any two monads M, N for which one can define some function M (N a) -> N (M a) will compose. In addition to defining a function with this type signature, one needs to prove that certain laws hold. In many cases, these laws do not hold.
There are even some monads that have two inequivalent transformers; one defined in a "regular" way and one "ad hoc". A simple example is the identity monad Id a = a; it has the regular transformer IdT m = m ("composed") and the irregular "ad hoc" one: IdT2 m a = forall r. (a -> m r) -> m r (the codensity monad on m).
A more complicated example is the "selector monad": Sel q a = (a -> q) -> a. Here q is a fixed type and a is the main type parameter of the monad Sel q. This monad has two transformers: SelT1 m a = (m a -> q) -> m a (composed-inside) and SelT2 m a = (a -> m q) -> m a (ad hoc).
Full details are worked out in Chapter 14 of the book "The Science of Functional Programming". https://github.com/winitzki/sofp or https://leanpub.com/sofp/
Here is some code making monad composition via a distributive law work. Note that there are distributive laws from any monad to the monads Maybe, Either, Writer and []. On the other hand, you won't find such (general) distributive laws into Reader and State. For these, you will need monad transformers.
{-# LANGUAGE FlexibleInstances #-}
module ComposeMonads where
import Control.Monad
import Control.Monad.Writer.Lazy
newtype Compose m1 m2 a = Compose { run :: m1 (m2 a) }
instance (Functor f1, Functor f2) => Functor (Compose f1 f2) where
fmap f = Compose . fmap (fmap f) . run
class (Monad m1, Monad m2) => DistributiveLaw m1 m2 where
dist :: m2 (m1 a) -> m1 (m2 a)
instance (Monad m1,Monad m2, DistributiveLaw m1 m2)
=> Applicative (Compose m1 m2) where
pure = return
(<*>) = ap
instance (Monad m1, Monad m2, DistributiveLaw m1 m2)
=> Monad (Compose m1 m2) where
return = Compose . return . return
Compose m1m2a >>= g =
Compose $ do m2a <- m1m2a -- in monad m1
m2m2b <- dist $ do a <- m2a -- in monad m2
let Compose m1m2b = g a
return m1m2b
-- do ... :: m2 (m1 (m2 b))
-- dist ... :: m1 (m2 (m2 b))
return $ join m2m2b -- in monad m2
instance Monad m => DistributiveLaw m Maybe where
dist Nothing = return Nothing
dist (Just m) = fmap Just m
instance Monad m => DistributiveLaw m (Either s) where
dist (Left s) = return $ Left s
dist (Right m) = fmap Right m
instance Monad m => DistributiveLaw m [] where
dist = sequence
instance (Monad m, Monoid w) => DistributiveLaw m (Writer w) where
dist m = let (m1,w) = runWriter m
in do a <- m1
return $ writer (a,w)
liftOuter :: (Monad m1, Monad m2, DistributiveLaw m1 m2) =>
m1 a -> Compose m1 m2 a
liftOuter = Compose . fmap return
liftInner :: (Monad m1, Monad m2, DistributiveLaw m1 m2) =>
m2 a -> Compose m1 m2 a
liftInner = Compose . return