2

Consider the following wrapper:

newtype F a = Wrap { unwrap :: Int }

I want to disprove (as an exercise to wrap my head around this interesting post) that there’s a legitimate Functor F instance which allows us to apply functions of Int -> Int type to the actual contents and to ~ignore~ all other functions (i. e. fmap nonIntInt = id).

I believe this should be done with a free theorem for fmap (which I read here): for given f, g, h and k, such that g . f = k . h: $map g . fmap f = fmap k . $map h, where $map is the natural map for the given constructor.

What defines a natural map? Am I right to assume that it is a simple flip const for F?

As far as I get it: $map f is what we denote as Ff in category theory. Thus, in a categorical sense, we simply want something among the lines of the following diagram to commute: enter image description here

Yet, I do not know what to put instead of ???s (that is, what functor do we apply to get such a diagram and how do we denote this almost-fmap?).

So, what is a natural map in general, and for F? What is the proper diagram for fmap's free theorem?


Where am I going with this?

Consider:

f = const 42
g = id

h    = const ()
k () = 42

It is easy to see that f . g is h . k. And yet, the non-existant fmap will execute only f, not k, giving different results. If my intuition about the naturality is correct, such a proof would work. That's what I am trying to figure out.

@leftaroundabout proposed a simpler piece of proof: fmap show . fmap (+1) alters the contents, unlike fmap $ show . (+1). It is a nice piece of proof, and yet I would still like to work with free theorems as an exercise.

Zhiltsoff Igor
  • 1,812
  • 8
  • 24
  • 2
    I don't see where you're going with this natural map stuff. You just need to show, for the functor instance you described, a counterexample where `fmap f . fmap g ≠ fmap (f . g)`. That's easy to do. – leftaroundabout Apr 24 '21 at 22:02
  • How does one write an `fmap f (Wrap x)` that distinguishes between `f :: Int -> Int` and all the other function types for `f`? – Micha Wiedenmann Apr 24 '21 at 22:54
  • 1
    @leftaroundabout oh, that's a good idea. So, we're taking `g = (+1)` and `f = show`, so `fmap (f . g)` does not alter the contents, unlike `fmap f . fmap g`. Thank you, and yet I still want to do this using the free theorem as, as I mentioned in the post, this is a mere exercise to understand this whole thing with free theorems :). Besides, if my understanding of *naturality* is correct, the proof using thee free theorem is not way harder: consider `f = const 42`, `g = id`, `h = const ()` and `k ~() = 42`. I will add both those proofs to the post. – Zhiltsoff Igor Apr 25 '21 at 07:49
  • @MichaWiedenmann one can't, and that's what we're trying to prove formally :). – Zhiltsoff Igor Apr 25 '21 at 07:49
  • `const 42` has type `Int->Int` and also `()->Int` and any type `a->Int` for all types `a`. What should `fmap (const 42) (Wrap 0)` be? – n. m. could be an AI Apr 25 '21 at 08:43
  • Parametricity is not dependent on Hask. If you want to prove a theorem about what *every* function of type `(a->b) -> Wrap a -> Wrap b` can and cannot do, you probably should open the Wadler's paper and see how such results are proved there. Whether such function is intended to serve as `fmap` is irrelevant to the eventual proof, thus no need to draw commutative diagrams and stuff. – n. m. could be an AI Apr 25 '21 at 09:00
  • @n.'pronouns'm. concerning your first comment: you are right, I took some liberty not to overburden the text. And yet, whether we allow `forall a. a -> Int` to be interpreted as `Int -> Int` or not, I don't think it will make much difference as the counterexamples would just involve same functions with more narrow or broad type signatures. – Zhiltsoff Igor Apr 25 '21 at 09:21
  • OTOH category theory doesn't depend on parametricity. The function you are talking about cannot serve as `fmap` even if you somehow manage to write non-parametric functions (for example, a compiler extension supplies you with a magic non-parametric function you can build upon). This is what @leftaroundabout proves. – n. m. could be an AI Apr 25 '21 at 09:24
  • You cannot write a function that depending on the type signature you supply produces different results. Is this what you want to prove? – n. m. could be an AI Apr 25 '21 at 09:26
  • @n.'pronouns'm. concerning your last comment: yes, that's right. In a particular case for a `fmap`. – Zhiltsoff Igor Apr 25 '21 at 09:29
  • So you probably should ask that question and get rid of the categorical red herring. – n. m. could be an AI Apr 25 '21 at 09:30
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/231571/discussion-between-zhiltsoff-igor-and-n-pronouns-m). – Zhiltsoff Igor Apr 25 '21 at 09:35

1 Answers1

3

So we are entertaining a function m :: forall a b . (a->b) -> F a -> F b such that (among other things)

m (1 +)    (Wrap x) = (Wrap (1+x))
m (show)   (Wrap x) = (Wrap x)

There are two somewhat related questions here.

  1. Can a well-behaved fmap do this?
  2. Can a parametric function do this?

The answer to both questions is "no".

A well-behaved fmap can't do this because fmap has to obey the axioms of Functor. Whether our environment is parametric or not is irrelevant. The axiom of Functor says that for all functions a and b, fmap (a . b) = fmap a . fmap b must hold, and this fails for a = show and b = (1 +). So m cannot be a well-behaved fmap.

A parametric function can't do this because that is what the parametricity theorem says. When viewing types as relations between terms, related functions take related arguments to related results. It is easy to see that m fails parametricity, but it is slightly easier to look at m': forall a b. (a -> b) -> (Int -> Int) (the two can be trivially converted to each other). (1 +) is related to show because m' is polymorphic in its argument, so different values of the argument can be related by any relation. Functions are relations, and there exists a function that sends (1 +) to show. However, the result type of m' has no type variables, so it corresponds to the constant relation (its values are only related to themselves). Since every value including m' is related to itself, it follows that all parametric functions m :: forall a b. (a -> b) -> (Int -> Int) must obey m f = m g, i.e. they must ignore their first argument. Which is intuitively obvious since there is nothing to apply it to.

One can in fact deduce the first statement from the second by observing that a well-behaved fmap must be parametric. So even if the language allows non-parametricity, fmap cannot make any non-trivial use of it.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • Aha, so, to sum up, my attempt to do this via categories was faulty as parametricity is not required by category theory (see your comment under the main post). The right way would've been to consider this `fmap` to be the functor's map for arrows and show that the `fmap (a . b) = fmap a . fmap b` law doesn't hold. Did I describe my mistake correctly? – Zhiltsoff Igor Apr 25 '21 at 14:11
  • @ZhiltsoffIgor In fact parametricity seems to be required for `fmap`, because `fmap (f . g) = fmap f . fmap g` is strictly stronger than the parametricity condition (I think; need to verify this...) – n. m. could be an AI Apr 25 '21 at 16:06
  • 1
    I believe that the second functor law follows from the first functor law plus the parametricity condition. – Zhiltsoff Igor Apr 25 '21 at 18:06
  • Also, can you, please touch upon the notion of **naturality** discussed in the question? The one about finding our whether `$map` for `F` is simply `flip const`. – Zhiltsoff Igor Apr 26 '21 at 09:05
  • 1
    Does [this here reddit post](https://www.reddit.com/r/haskell/comments/2w05a0/edward_kmett_the_free_theorem_for_fmap/) help? – n. m. could be an AI Apr 26 '21 at 11:24
  • It does, thank you. Yet, I don't feel too confident about fully getting it. Not to extend our discussion here, I made [a post about it](https://stackoverflow.com/questions/67314984/natural-map-derivation-algorithm). – Zhiltsoff Igor Apr 29 '21 at 11:49