What's the difference between Applicatives and Monads?
|Law||definition ||definition ||definition |
What is the difference in
High level understanding:
key difference is in the second argument.
| | Monads
(>>=) | Applicatives
(<**>) | | full type signature |
m a -> (a -> m b) -> m b |
f a -> f (a -> b) -> f b | | second argument |
a -> m b |
f (a -> b) | | difference | First argument,
a can decide the future computation
a is already in the context
Examples for intuition
Monadic instance for Either:
instance Monad (Either e) where return = Right v@(Left e) >>= _ = v Right a >>= f = f a
We observe that:
Left "Failed 0" >>= \_ -> Left "Failed 1" = Left "Failed 0"
We have lost the information:
Left "Failed 1"
We might then be tempted to tweak the monad definition, to permit collecting error messages:
instance Monoid e => Monad (Either e) where return = Right Left e >>= f = ?? Right a >>= f = f a
What do we want to do? We want to apply f to a value:
f a. We can then pattern match on the result:
Left e >>= f = case f a of Left e' -> Left (e <> e') Right _ -> Left e
We realize that we can’t do that however.
Left e is part of the context and does not return any result, in contrast to
f only takes in
a as a functional dependency (since
f :: a -> m b) The context of the computation (
f a in
f a -> (a -> m b)) is opaque to it.
As such we can only do 2 things:
- If we can get a result, pass it into f.
- If we can’t get a result, short circuit, since we can’t evaluate f in any way.
Wait a minute, you say.
What if we did this:
Left e >>= \_ -> Left e' = Left (e <> e')
We can’t do that either because we cannot go into function bodies! We can only see the mapping between input and the result.
What about this then!
Left e >> Left e = Left (e <> e')
Yes we could do that.
Note however, this is not powerful enough to give us a monad.
We are unable to define
(>>=) in terms of
(>>) (although the vice-versa works).
(Possible) Applicative instance for Either:
instance Monoid e => Applicative (Either e) where pure = Right Left e <*> Left e' = Left (e <> e') Left e <*> Right a = Right a Right f <*> Left e' = Left e' Right f <*> Right a = Right $ f a
This is because
Applicative interface separate the context layer (
f (a -> b)) rather than (
a -> f b).
This makes the
(<*>) operator able to work on the contexts as well.
Further examination of applicative patterns
Let’s examine two types of computations:
Here, we can see that p1, p2 are not dependent on previous result. The final result uses
return which means it does not introduce any new context.
do val1 <- p1 val2 <- p2 return $ f val1 val2
We can thus represent this in applicative form:
pure f <*> val1 <*> val2
Here we can see a dependence:
do val1 <- p1 val2 <- p2 val1 -- p2 is dependent on the result of p1 return val2
Thus, this requires a monad.
Closed under composition
m1 (m2 a) may or may not be a monad even if m1, m2 are monads.
a1 (a2 a) is an applicative as long as a1, a2 are applicatives.
References: Stack Overflow