From the documentation of the validation
package:
The
AccValidation
data type is isomorphic toEither
, but has an instance ofApplicative
that accumulates on the error side. That is to say, if two (or more) errors are encountered, they are appended using aSemigroup
operation.As a consequence of this
Applicative
instance, there is no correspondingBind
orMonad
instance.AccValidation
is an example of, "An applicative functor that is not a monad."
It isn't evident to me why this is a consequence. I can imagine a Monad
instance for AccValidation
that behaves like Either
- What would make this unlawful?
The
(<*>) = ap
exigence can be stated explicitly in terms of(>>=)
:Now, given the
Functor
andApplicative
instances forAccValidation
, we have:If we make
u = AccFailure e1
andv = AccFailure e2
in [1], we get:Substituting [2] and [3] into that leads us to:
The problem is that it is impossible to write a
(>>=)
such that [4] holds. The left-hand side depends on ane2
value which must originate, on the right-hand side, from applying\_ -> AccFailure e2 :: Semigroup e => a -> AccValidation e b
. However, there is nothing to which it can be applied -- in particular,e1
has the wrong type. (See the final two paragraphs of Cactus' answer for further discussion of this point.) Therefore, there is no way of givingAccValidation
aMonad
instance which is consistent with itsApplicative
one.Mechanically, the
Either
-ishMonad
instance forAccValidation
would bewhich would mean we have
which breaks the monad law of
<*> = ap
.Intuitively, it can't be made a monad because in a monad, the effect (i.e. the validation failures) of a computation can depend on previously bound results. But in the case of a failure, there is no result. So
Either
has no choice than to short-circuit to failure in that case, since there is nothing to feed to the subsequent functions on right-hand sides of(>>=)
s.This is in stark contrast to applicative functors, where the effect (in this case, the validation failures) cannot depend on other results, which is why we can get all validation failures without having to feed results (where would they come from?) from one computation to the other.