Consider the monadic function composition operator <=<
. This is analogous to .
except it works on monadic functions. It can be defined simply in terms of >>=
, so learning about one will educate us about the other.
(<=<) :: (a -> m b) -> (b -> m c) -> a -> m c
(f <=< g) x = g x >>= f
(.) :: (a -> b) -> (b -> c) -> a -> c
(f . g) x = g x |> f
where z |> h = h z
In the case of .
, g
is "performed" first, and then f
is performed on the output of g
. In the case of <=<
, g
and its effects are "performed" first, and then f
and its effects are performed. It's a bit of a misnomer to say that one happens "before" the other, actually, since not all monads work that way.
Perhaps it is more accurate to say that f
can take advantage of additional contextual information provided by g
. But that's not entirely correct, since g
could potentially take away contextual information. If you want to 100% correctly describe monads, you really have to walk on eggshells.
But in almost all nontrivial cases, f <=< g
means that the effects (as well as the result) of the monadic function g
will subsequently influence the behavior of the monadic function f
.
To address questions about v >>= f = join (fmap f v)
Consider f :: a -> m b
and v :: m a
. What does it mean to fmap f v
? Well fmap :: (c -> d) -> m c -> m d
, and in this case c = a
and d = m b
, so fmap f :: m a -> m (m b)
. Now, of course, we can apply v :: m a
to this function, resulting in m (m b)
. but what exactly does that result type m (m b)
mean?
The inner m
represents the context from produced from f
. The outer m
represents the context originating from v
(n.b. fmap
should not disturb this original context).
And then you join
that m (m b)
, smashing those two contexts together into m a
. This is the heart of the definition of Monad
: you must provide a way to smash contexts together. You can inspect the implementation details of various Monad
instances to try and understand how they "smash" contexts together. The takeaway here, though, is that the "inner context" is unobservable until you merge it with the "outer context". If you use v >>= f
, then there is no actual notion of the function f
receiving a pure value a
and producing a simple monadic result m b
. Instead, we understand that f
acts inside the original context of v
.