According to Haskell's library documentation, every instance of the Applicative class must satisfy the four rules:
pure id <*> v = v
pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
pure f <*> pure x = pure (f x)
u <*> pure y = pure ($ y) <*> u
It then says that as a consequence of these rules, the underlying Functor instance will satisfy fmap f x = pure f <*> x
. But since the method fmap
does not even appear in the above equations, how exactly does this property follow from them?
Update: I've substantially expanded the answer. I hope it helps.
For any functor F
, there is a "free theorem" (see below) for the type:
(a -> b) -> F a -> F b
This theorem states that for any (total) function, say foo
, with this type, the following will be true for any functions f
, f'
, g
, and h
, with appropriate matching types:
If
f' . g = h . f
, thenfoo f' . fmap g = fmap h . foo f
.
Note that it is not at all obvious why this should be true.
Anyway, if you set f = id
and g = id
and use the functor law fmap id = id
, this theorem simplifies to:
For all
h
, we havefoo h = fmap h . foo id
.
Now, if F
is also an applicative, then the function:
foo :: (a -> b) -> F a -> F b
foo f x = pure f <*> x
has the right type, so it satisfies the theorem. Therefore, for all h
, we have:
pure h <*> x
-- by definition of foo
= foo h x
-- by the specialized version of the theorem
= (fmap h . foo id) x
-- by definition of the operator (.)
= fmap h (foo id x)
-- by the definition of foo
= fmap h (pure id <*> x)
-- by the identity law for applicatives
= fmap h x
In other words, the identity law for applicatives implies the relation:
pure h <*> x = fmap h x
It is unfortunate that the documentation does not include some explanation or at least acknowledgement of this extremely non-obvious fact.
Originally, the documentation listed the four laws (identity, composition, homomorphism, and interchange), plus two additional laws for *>
and <*
and then simply stated:
The
Functor
instance should satisfyfmap f x = pure f <*> x
The wording above was replaced with the new text:
As a consequence of these laws, the
Functor
instance forf
will satisfyfmap f x = pure f <*> x
as part of commit 92b562403 in February 2011 in response to a suggestion made by Russell O'Connor on the libraries list.
Russell pointed out that this rule was actually implied by the other applicative laws. Originally, he offered the following proof (the link in the post is broken, but I found a copy on archive.org). He pointed out that the function:
possibleFmap :: Applicative f => (a -> b) -> f a -> f b
possibleFmap f x = pure f <*> x
satisfies the Functor laws for fmap
:
pure id <*> x = x {- Identity Law -} pure (f . g) <*> x = {- infix to prefix -} pure ((.) f g) <*> x = {- 2 applications of homomorphism law -} pure (.) <*> pure f <*> pure g <*> x = {- composition law -} pure f <*> (pure g <*> x)
and then reasoned that:
So,
\f x -> pure f <*> x
satisfies the laws of a functor. Since there is at most one functor instance per data type,(\f x -> pure f <*> x) = fmap
.
A key part of this proof is that there is only one possible functor instance (i.e., only one way of defining fmap
) per data type.
When asked about this, he gave the following proof of the uniqueness of fmap
.
Suppose we have a functor
f
and another functionfoo :: (a -> b) -> f a -> f b
Then as a consequence of the free theorem for
foo
, for anyf :: a -> b
and anyg :: b -> c
.foo (g . f) = fmap g . foo f
In particular, if
foo id = id
, thenfoo g = foo (g . id) = fmap g . foo id = fmap g . id = fmap g
Obviously, this depends critically on the "consequence of the free theorem for foo
". Later, Russell realized that the free theorem could be used directly, together with the identity law for applicatives, to prove the needed law. That's what I've summarized in my "short answer" above.
So what about this "free theorem" business?
The concept of free theorems comes from a paper by Wadler, "Theorems for Free". Here's a Stack Overflow question that links to the paper and some other resources. Understanding the theory "for real" is hard, but you can think about it intuitively. Let's pick a specific functor, like Maybe
. Suppose we had a function with the following type;
foo :: (a -> b) -> Maybe a -> Maybe b
foo f x = ...
Note that, no matter how complex and convoluted the implementation of foo
is, that same implementation needs to work for all types a
and b
. It doesn't know anything about a
, so it can't do anything with values of type a
, other than apply the function f
, and that just gives it a b
value. It doesn't know anything about b
either, so it can't do anything with a b
value, except maybe return Just someBvalue
. Critically, this means that the structure of the computation performed by foo
-- what it does with the input value x
, whether and when it decides to apply f
, etc. -- is entirely determined by whether x
is Nothing
or Just ...
. Think about this for a bit -- foo
can inspect x
to see if it's Nothing
or Just someA
. But, if it's Just someA
, it can't learn anything about the value someA
: it can't use it as-is because it doesn't understand the type a
, and it can't do anything with f someA
, because it doesn't understand the type b
. So, if x
is Just someA
, the function foo
can only act on its Just
-ness, not on the underlying value someA
.
This has a striking consequence. If we were to use a function g
to change the input values out from under foo f x
by writing:
foo f' (fmap g x)
because fmap g
doesn't change x
's Nothing
-ness or Just
-ness, this change as no effect on the structure of foo
's computation. It behaves the same way, processing the Nothing
or Just ...
value in the same way, applying f'
in exactly the same circumstances and at exactly the same time that it previously applied f
, etc.
This means that, as long as we've arranged things so that f'
acting on the g
-transformed value gives the same answer as an h
-transformed version of f
acting on the original value -- in other words if we have:
f' . g = h . f
then we can trick foo
into processing our g
-transformed input in exactly the same way it would have processed the original input, as long as we account for the input change after foo
has finished running by applying h
to the output:
foo f' (fmap g x) = fmap h (foo f x)
I don't know whether or not that's convincing, but that's how we get the free theorem:
If
f' . g = h . f
thenfoo f' . fmap g = fmap h . foo f
.
It basically says that because we can transform the input in a way that foo
won't notice (because of its polymorphic type), the answer is the same whether we transform the input and run foo
, or run foo
first and transform its output instead.