I\'ve read this article, but didn\'t understand last section.
The author says that Monad gives us context sensitivity, but it\'s possible to achieve the same result
Now that ApplicativeDo
extension become pretty common thing, the difference between Monad
and Applicative
can be illustrated using simple code snippet.
With Monad
you can do
do
r1 <- act1
if r1
then act2
else act3
but having only Applicative
do-block, you can't use if
on things you've pulled out with <-
.
On the other hand, here's a a practical example of the Applicative
/Monad
divide where Applicative
s have an advantage: error handling! We clearly have a Monad
implementation of Either
that carries along errors, but it always terminates early.
Left e1 >> Left e2 === Left e1
You can think of this as an effect of intermingling values and contexts. Since (>>=)
will try to pass the result of the Either e a
value to a function like a -> Either e b
, it must fail immediately if the input Either
is Left
.
Applicative
s only pass their values to the final pure computation after running all of the effects. This means they can delay accessing the values for longer and we can write this.
data AllErrors e a = Error e | Pure a deriving (Functor)
instance Monoid e => Applicative (AllErrors e) where
pure = Pure
(Pure f) <*> (Pure x) = Pure (f x)
(Error e) <*> (Pure _) = Error e
(Pure _) <*> (Error e) = Error e
-- This is the non-Monadic case
(Error e1) <*> (Error e2) = Error (e1 <> e2)
It's impossible to write a Monad
instance for AllErrors
such that ap
matches (<*>)
because (<*>)
takes advantage of running both the first and second contexts before using any values in order to get both errors and (<>)
them together. Monad
ic (>>=)
and (join)
can only access contexts interwoven with their values. That's why Either
's Applicative
instance is left-biased, so that it can also have a harmonious Monad
instance.
> Left "a" <*> Left "b"
Left 'a'
> Error "a" <*> Error "b"
Error "ab"