Given:
Applicative m, Monad m => mf :: m (a -> b), ma :: m a
it seems to be considered a law that:
mf <*> ma === do { f <- mf; a <- ma; return (f a) }
or more concisely:
(<*>) === ap
The documentation for Control.Applicative
says that <*>
is "sequential application," and that suggests that (<*>) = ap
. This means that <*>
must evaluate effects sequentially from left to right, for consistency with >>=
... But that feels wrong. McBride and Paterson's original paper seems to imply that the left-to-right sequencing is arbitrary:
The IO monad, and indeed any Monad, can be made Applicative by taking
pure
=return
and<*>
=ap
. We could alternatively use the variant ofap
that performs the computations in the opposite order, but we shall keep to the left-to-right order in this paper.
So there are two lawful, non-trivial derivations for <*>
that follow from >>=
and return
, with distinct behavior. And in some cases, neither of these two derivations are desirable.
For example, the (<*>) === ap
law forces Data.Validation to define two distinct data types: Validation
and AccValidation
. The former has a Monad
instance similar to ExceptT, and a consistent Applicative
instance which is of limited utility, since it stops after the first error. The latter, on the other hand, doesn't define a Monad
instance, and is therefore free to implement an Applicative
that, much more usefully, accumulates errors.
There's been some discussion about this previously on StackOverflow, but I don't think it really got to the meat of the question:
Why should this be a law?
The other laws for functors, applicatives and monads—such as identity, associativity, etc.—express some fundamental, mathematical properties of those structures. We can implement various optimizations using these laws and prove things about our own code using them. In contrast, it feels to me like the (<*>) === ap
law imposes an arbitrary constraint with no corresponding benefit.
For what it's worth, I'd prefer to ditch the law in favor of something like this:
newtype LeftA m a = LeftA (m a)
instance Monad m => Applicative (LeftA m) where
pure = return
mf <*> ma = do { f <- mf; a <- ma; return (f a) }
newtype RightA m a = RightA (m a)
instance Monad m => Applicative (RightA m) where
pure = return
mf <*> ma = do { a <- ma; f <- mf; return (f a) }
I think that correctly captures the relationship between the two, without unduly constraining either.
So, a few angles to approach the question from:
- Are there any other laws relating
Monad
andApplicative
? - Is there any inherent mathematical reason for effects to sequence for
Applicative
in the same way that they do forMonad
? - Does GHC or any other tool perform code transformations that assume/require this law to be true?
- Why is the Functor-Applicative-Monad proposal considered such an overwhelmingly good thing? (Citations would be much appreciated here).
And one bonus question:
- How do
Alternative
andMonadPlus
fit in to all this?
Note: major edit to clarify the meat of the question. Answer posted by @duplode quotes an earlier version.
Well, I'm not terribly satisfied with the answers given so far, but I think the comments attached to them are a bit more compelling. So I'll summarize here:
I think there's only one sensible
Functor
instance that follows fromApplicative
:Assuming that's unique, it makes sense that
Functor
should be a superclass ofApplicative
, with that law. Likewise, I think there's only one sensibleFunctor
instance that follows fromMonad
:So again, it makes sense that
Functor
should be a superclass ofMonad
. The objection I had (and, really, still have) is that there are two sensibleApplicative
instances that follow fromMonad
and, in some specific instances, even more that are lawful; so why mandate one?pigworker (first author on the original
Applicative
paper) writes:duplode similarly writes:
So, I'm happy to see that choice stated explicitly, justified by the simple reasoning that it makes the most common cases easier.
Just for the record, the answer to the question in the title is: consider
What is the type of
join . sequenceA
?Monad m, Traversable m => m (m a) -> m a
Applicative m, Monad m, Traversable m => m (m a) -> m a
Granted,
join . sequenceA
is a contrived situation, but there are certainly cases where you need a monad, but you'd also like to use theApplicative
operations<*>
,*>
,<*
,<**>
, etc. Then:Applicative
names is (IMHO) nicer than those of the traditional monad operations.ap
,>>
,<<
, etc., is annoying ("oh, you can't use<*>
there, that's aMonad
not anApplicative
"; "oh, you have to use<*>
there, that's anApplicative
not aMonad
").>>
and*>
do different things, then you can't actually use theApplicative
syntax, because it'll do something you don't expect.So, pragmatically, having an
Applicative
for everyMonad
which is compatible with it (in the(<*>) = ap
sense) is a really, really good idea.Among other things, you ask why is the
Functor-Applicative-Monad
proposal a good thing. One reason is because the lack of unity means there is a lot of duplication of API. Consider the standardControl.Monad
module. The following are the functions in that module that essentially use theMonad
(there are none forMonadPlus
) constraint:The following are the functions in that module where a
Monad
/MonadPlus
constraint could as far as I can tell easily be relaxed toApplicative
/Alternative
:Many of the latter group do have
Applicative
orAlternative
versions, in eitherControl.Applicative
,Data.Foldable
orData.Traversable
– but why need to learn all that duplication in the first place?The values don't, but the effects do.
(<*>) :: t (a -> b) -> t a -> t b
means that you have to somehow combine the effects of the arguments in order to get the overall effects. Whether the combination will be commutative or not depends on how the instance is defined. For example, the instance forMaybe
is commutative, while the default, "cross join" instance for lists isn't. Therefore, there are cases in which you can't avoid imposing some order.While it is fair to say that
pure === return
and(<*>) === ap
(quotingControl.Applicative
) aren't laws in the strong sense that e.g. the monad laws are so, they help keeping the instances unsurprising. Given that everyMonad
gives rise to an instance ofApplicative
(actually two instances, as you point out), it is natural that the actual instance ofApplicative
matches whatMonad
gives us. As for the left-to-right convention, following the order ofap
andliftM2
(which already existed back whenApplicative
was introduced, and which mirror the order imposed by(>>=)
) was a sensible decision. (Note that, if we ignored for a moment how much(>>=)
matters in practice, the opposite choice would be defensible as well, as it would make(<*>)
and(=<<)
, which have analogous types, sequence effects in the same order.)That sounds very unlikely given that
Applicative
isn't even a superclass ofMonad
(yet). These "laws", however, allow readers of the code to make the transformations, which matters just as much.N.B.: If you need to reverse the sequencing of effects in an
Applicative
instance, there isControl.Applicative.Backwards
, as Gabriel Gonzalez has pointed out. Also,(<**>)
flips the arguments but still sequences effects from left to right, so it can also be used to reverse sequencing. Similarly,(<*)
is notflip (*>)
, as both sequence effects from left to right.