Why do monads not compose when a Monad is an Applicative and an Applicative is a Functor. You see this inheritance chain in many articles on the web ( Which i have gone through ). But when Functors and Applicatives compose why do Monads break this ?
Can someone provide a simple example in scala which demonstrates this issue ? I know this is asked a lot but kind of hard to understand without a simple example.
Tony Morris gave a talk on monad transformers that explains this precise issue very well.
http://tonymorris.github.io/blog/posts/monad-transformers/
He uses haskell, but the examples are easily translatable to scala.
First, let's start with a simple problem. Let's say, we need to get a sum of two integers, each wrapped in both
Future
andOption
. Let's takecats
library in order to resemble Haskell’s standard library definitions with Scala-syntax.If we use monad approach (aka
flatMap
), we need:Future
andOption
should haveMonad
instances defined over themOptionT
which will work only forOption
(preciselyF[Option[T]]
)So, here is the code (let's forget about for-comprehension and lifting to make it simpler):
if you look at
OptionT.flatMap
sources:You'll notice that the code is pretty specific to
Option
's internal logic and structure (fold
,None
). Same problem forEitherT
,StateT
etc.Important thing here is that there is no
FutureT
defined in cats, so you can composeFuture[Option[T]]
, but can't do that withOption[Future[T]]
(later I'll show that this problem is even more generic).On the other hand, if you choose composition using
Applicative
, you'll have to meet only one requirement:Future
andOption
should haveApplicative
instances defined over themYou don't need any special transformers for
Option
, basically cats library providesNested
class that works for anyApplicative
(let's forget about applicative builder's sugar to simplify understanding):Let's swap Option and Future:
Works!
So yes Monad is Applicative,
Option[Future[T]]
is still a monad (onFuture[T]
but not onT
itself) but it allows you to operate only withFuture[T]
notT
. In order to "merge"Option
withFuture
layers - you have to define monadic transformerFutureT
, in order to mergeFuture
withOption
- you have to defineOptionT
. And,OptionT
is defined in cats/scalaz, but notFutureT
.In general (from here):
And this composition is not even necessary commutative (swappable) as I demonstrated for
Option
andFuture
.As an exercise, you can try to define
FutureT
's flatMap:basically the problem with such implementation is that you have to "extract" value from r which is impossible here, assuming you can't extract value from
Future
(there is no comonad defined on it) at least in a "non-blocking" context (like ScalaJs). This basically means that you can't "swap"Future
andF
, likeFuture[F[Future[B]] => F[Future[Future[B]
. The latter is a natural transformation (morphism between functors), so that explains the first comment on this general answer:Applicative
s however don't have such problems - you can easily compose them, but keep in mind that result of composition of twoApplicatives
may not be a monad (but will always be an applicative).Nested[Future, Option, T]
is not a monad onT
, regardless that bothOption
andFuture
are monads onT
. Putting in simple words Nested as a class doesn't haveflatMap
.It would be also helpful to read:
Putting it all together (
F
andG
are monads)F[G[T]]
is a monad onG[T]
, but not onT
G_TRANSFORMER[F, T]
required in order to get a monad onT
fromF[G[T]]
.MEGA_TRANSFORMER[G, F, T]
as such transformer can't be build on top of monad - it requires additional operations defined onG
(it seems like comonad onG
should be enough)G
andF
) is applicative, but not every applicative is a monadF[G[T]]
is an applicative over bothG[T]
andT
. However scala requires to createNESTED[F, G, T]
in order to get composed applicative onT
(which is implemented in cats library).NESTED[F, G, T]
is applicative, but not a monadThat means you can compose
Future x Option
(akaOption[Future[T]]
) to one single monad (cozOptionT
exists), but you can't composeOption x Future
(akaFuture[Option[T]]
) without knowing that Future is something else besides being a monad (even though they’re inherently applicative functors - applicative is not enough to neither build a monad nor monad transformer on it) . Basically:OptionT
can be seen as non-commutative binary operator defined asOptionT: Monad[Option] x Monad[F] -> OptionT[F, T]; for all Monad[F], T; for some F[T]
. Or in general:Merge: Monad[G] x Monad[F] -> Monad[Merge]; for all T, Monad[F]; but only for **some of Monad[G]**, some F[T], G[T]
;you can compose any two applicatives into one single applicative
Nested: Applicative[F] x Applicative[G] -> Nested[F, G]; for all Applicative[F], Applicative[G], T; for some F[T], G[T]
,but you can compose any two monads (inherently functors) only into one applicative (but not into monad).