2
$\begingroup$

When we defining a Functor instance in Haskell, it should satisfy the following two laws:

  • fmap id = id
  • fmap f . fmap g = fmap (f . g)

But I found these two articles that say the second Functor law can be derived form the first:

The proof in these articles uses the free theorem for fmap. For example, the second article says that:

Free Theorem:

The free theorem for fmap :: (a -> b) -> F a -> F b is that given functions f, g, k, and h such that

g . h = k . f

then

$map g . fmap h = fmap k . $map f

where $map is the "natural map" for the type constructor F.

Proof:

This is a free theorem, so it holds for any function with the same type signature as fmap, regardless of implementation.

You can obtain this theorem employing Philip Wadler's “Theorems for Free” laboriously by hand as is done by Bartosz in the comments below, but we can also obtain this result just by asking lambdabot to do it for us on IRC.

<edwardk> @free fmap :: (a -> b) -> (F a -> F b)
<lambdabot> g . h = k . f => $map_F g . fmap h = fmap k . $map_F f

Thanks, lambdabot!

I cannot find Bartosz's comment, so I found and read Philip Wadler's “Theorems for Free”.

Philip Wadler's “Theorems for Free” basically says that for each type $T$, there is a corresponding relation $\mathcal{T}$, and every closed term $t$ of type $T$ satisfies $(t,t)\in\mathcal{T}$.

The type constructor $F$ of the Functor would also give a function $\mathcal{F}$ from relations to relations. This is the "natural map" $map in the article. But the problem is, this $map does not always map functions to functions.

When I tried to derive fmap's free theorem by hand, I got this:

For given functions f :: A -> B, g :: C -> D, k :: B -> D, h :: A -> C such that

g . h = k . f

then for every x :: F A and y :: F C, if (x, y) is in the relation $map h, then (fmap f x, fmap g y) is in the relation $map g.

If we can show that $map actually maps functions to functions, this result would be the same as that in the article.

But there are some type constructors that $map doesn't maps functions to functions. For example, for F a = a -> (), $map would map functions to functions in the inverse direction.

How do I fill this gap of the proof?

$\endgroup$

2 Answers 2

2
$\begingroup$

The same way as in this question, the gap is once again the identity extension property. This means that the relational interpretation of a type operator maps identity relations to identity relations. This property is not mentioned in Wadler's paper, but it is in Reynolds'. It's needed whenever we want to reason about parameterized types.

The assumption of a "natural map" looks like an error to me, for the reasons you mention; arbitrary type operators are not functors.

Let's look at the proof. I write the relational interpretation as ᴿ superscripts, so that e.g. fmapᴿ denotes the free theorem for fmap. We assume F : * → * and fmap : ∀ A B. (A → B) → F A → F B with fmap A A id x = x. The goal is

fmap A C (f ∘ g) x = fmap B C f (fmap A B g x)

Now, by identity extension for F, I know that Fᴿ (Id C) = Id (F C), where Id A is the identity relation on A. So it's enough to show that the sides are related in Fᴿ (Id C). We apply fmapᴿ to get this.

  • We have to pick a relation between A and B: let this be the graph of g.
  • The relation between C and C must be the identity relation.
  • Now, it's trivial to show that f ∘ g and f take related inputs to related outputs.

It only remains to show (x, fmap A B g x) ∈ Fᴿ (Graph g). This is equivalent to

(fmap A A id x, fmap A B g x) ∈ Fᴿ (Graph g)

We use fmapᴿ once again.

  • We choose the relation between A and A to be the identity.
  • We choose the relation between A and B to be Graph g.
  • Once again relatedness of id and g is trivial.
  • We have to show (x, x) ∈ Fᴿ (Id A), but by identity extension that's just (x, x) ∈ Id (F A).
$\endgroup$
1
  • $\begingroup$ In my view, neither Wadler's paper nor Reynolds's paper are clear about what to do with arbitrary type constructors. So far, I have not found a clear tutorial about how to prove things relationally. In this case, it seems to be much easier for programmers to write directly a naturality law of fmap and show that the composition law follows. Then it remains to understand how to derive naturality laws from parametricity. But, at least, one doesn't have to guess how to choose relations, use identity extension lemma,etc. - naturality laws are already equations between functions. $\endgroup$
    – winitzki
    Dec 14, 2022 at 14:45
3
$\begingroup$

Andras is correct here. In the interest of full disclosure, I'm the original author of lambdabot and the free theorem plugin, and so I'm in a unique position to tell you exactly what's going on.

The way the plugin works is that it assumes that a type constructor with a single argument is a functor, and generates a free theorem using its map operation.

So, for example, suppose that the arity-1 type constructor is F, then it calls the map operation $map_F. There is a special case for lists; then it just calls it $map.

I made this assumption for a simple reason: it's an IRC bot. I didn't want to bother users with having to write this:

@free my_function :: (Functor f) => (a -> b) -> (f a -> f b)

Instead, they should be able to just write this:

@free my_function :: (a -> b) -> (F a -> F b)

If you do that, you get an answer like this:

g . h = k . f => $map_F g . my_function h = my_function k . $map_F f

Note that I called it my_function in this example rather than fmap to emphasise the fact that this is not the fmap operation for the functor type F. The fmap operation for F is called $map_F.

So, to repeat the important part of the second paragraph of this answer. Lambdabot assumes that a type constructor F with a single argument is a functor, and gives the fmap operation for that functor the name $map_F.

And what, might you ask, does it mean to assume that F is a functor? Well, it means that it has a "natural map" operation, and that this "natural map" operation obeys the functor laws.

So what this theorem is actually stating is that if F is a functor, then for any function my_function :: (a -> b) -> (F a -> F b) such that my_function id = id, then my_function is really that type's fmap operation. Or, more generally, that my_function is completely determined by what it does when you apply it to id.

It does not prove anything if F is not a functor.

It is not difficult to come up with arity-1 type constructors that are not functors. (Exercise: Do this! As a hint, try a cofunctor.) If you ----------have such a constructor, the assumption that lambdabot made is incorrect, and so the free theorem that lambdabot claims isn't true.

I'll try to get this fixed.

EDIT

I contacted Edward Kmett, who wrote that article in School of Haskell for fpcomplete (we've known each other for decades), and apparently it's now effectively archived and closed for editing. So there's apparently not much he can do about the mistake in the article.

$\endgroup$
1
  • $\begingroup$ The moral of the story: Don't use IRC bots to prove your theorems! $\endgroup$
    – Pseudonym
    Oct 24, 2022 at 1:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.