If you’re wondering how Right … <*> Left …
can still return a Left
, it’s because of the fmap
call in this definition:
instance Applicative (Either e) where
pure = Right
Left e <*> _ = Left e
Right f <*> r = fmap f r
If we expand the definition of fmap
for Either
, then the definition of <*>
looks like this:
Left e <*> _ = Left e
Right f <*> r = case r of
Left e -> Left e
Right x -> Right (f x)
Or, written more symmetrically with all the cases spelled out explicitly:
Left e1 <*> Left _e2 = Left e1 -- 1
Left e <*> Right _x = Left e -- 2
Right _f <*> Left e = Left e -- 3
Right f <*> Right x = Right (f x) -- 4
I’ve marked with an underscore _
the values that are discarded.
Notice that the only case that returns Right
is when both inputs are Right
. In fact, that’s the only time it’s possible to return Right
.
In case (4) we only have a Right (f :: a -> b)
and a Right (x :: a)
; we don’t have an e
, so we can’t return a Left
, and the only way we have to obtain a b
is by applying f
to x
.
In cases (1), (2), and (3), we must return a Left
, because at least one of the inputs is Left
, so we are missing the a -> b
or the a
that we would need to produce a b
.
When both inputs are Left
in case (1), Either
is biased toward the first argument.
There is a type similar to Either
called Validation
which combines its “failure” cases, instead of choosing one or the other, but it’s more constrained: it’s only an Applicative
, while Either
is both an Applicative
and a Monad
.