This is a follow up of my last question. IO action nested in other monads not executing
The solution to that question was to remove some of the monads, and that all
Well ... you ask the question:
Why did I need to unnest the monads? Is there a way to execute the IO without unnesting?
Well, let me put it as blunt as possibe: Unnesting or join
ing how we call it in Haskell-land, is not just yet another monad combinator, it is a holy special moand thing that differentiates Monad
s from Functor
and Applicative
!
Yes, that totally means that the Monad
type class could have been designed with a join
method instead of >>=
.
Actually unnesting and binding are two different perspectives for the same thing!
Let me spoil the rest of this post:
join = (>>= id)
... and:
(ma >>= amb) = join (amb <$> ma)
Let's prove them equal by showing that we can make a join
of >>=
and vice versa.
join
from >>=
Ok now join = (>>= id)
in more detail:
join mmx = do mx <- mmx
x <- mx
return x
and then:
join mmx = do mx <- mmx
mx
and now with bind
aka >>=
:
join mmx = mmx >>= id
and point free using sections:
join = (>>= id)
>>=
from join
Now the other way around, it is harder, we need that fact that every Monad
is a Functor
too.
Remember what >>=
actaully does (pun intended):
ma >>= amb = do a <- ma
amb a
We know that amb
is a function of type a -> m b
, the idea is to use fmap
which has (c -> d) -> m c -> m d
, if we fmap
amb
we get an expression fmap amb
then c
becomes a
and d
becomes m b
and m c -> m d
hence becomes m a -> m (m b)
!
Now we are thrilled: m (m b)
just screams join
, we only need to put in the m a
which we can do with normal application:
ma >>= amb = do mb <- fmap amb ma
mb
and this is something we saw in the previous section:
join mmb = do mb <- mmb
mb
with mmb == fmap amb ma
ma >>= amb == do mb <- fmap amb ma == join (fmap amb ma)
mb
There you go.