Exercises in The Parametricity Theorem

Functions

If e :: ∀a,b. a → b → a, then for all types A,B: for all x::A, y::B : x = e x y

Meaning: e must be const.

If e :: ∀a,b. (a → b) → a → b, then for all types A,B: for all f :: A → B, x :: A : f x = e f x

Meaning: e must be function application.

If e :: ∀a,b. (a → a → b) → a → b, then for all types A,B: for all op :: A → A → B, x :: A : op z z = e op z

Impossible = Wait Forever

Reminder: We assume that all values and functions are total (i.e., they terminate).

If e :: ∀a. a, then for all type A: for all x::A : x = eA
Further deduce that e should not exist. (Hint: Here is one way: Deduce both eBool = False and eBool = True.)

If e :: ∀a,b. a → b, then for all types A,B: for all x::A, y::B : y = e x
Furthe deduce that e should not exist.

Fancy Constants

If e :: ∀a. (Integer → a) → a, then for all type A, f :: Integer → A: f (e id) = e f

Meaning: e must hide a secret constant and apply f to it; the secret can be discovered by e id.

Tuples

Definition of ⟨(a,b)⟩

Idea: ⟨(a,b)⟩ relates two tuples componentwise.

Rigorously: (xl,yl) ⟨(a,b)⟩ (xr,yr) iff xl⟨a⟩xr and yl⟨b⟩yr.

Pragmatically: If ⟨a⟩ is a function f and ⟨b⟩ is a function g, then ⟨(a,b)⟩ simplifies to: pmap f g, defined by:
pmap f g (x,y) = (f x, g y)

Exercises:

If e1 :: ∀a,b. (a,b) → a, then for all types A,B: for all x::A, y::B : e1 (x,y) = x

If e2 :: ∀a,b. (a,b) → b, then for all types A,B: for all x::A, y::B : e2 (x,y) = y

Meaning: We can define “fst (x,y) = x”, and fst will have the same type as e1. What you are proving is that e1 must also be equivalent to fst. Similarly for e2.

Maybes

Definition of ⟨Maybe a⟩

Idea: xm ⟨Maybe a⟩ ym iff both are Nothing, or, both are Just and the pair of elements are related by ⟨a⟩.

Rigorously: ⟨Maybe a⟩ is the smallest relation such that:

Pragmatically: If ⟨a⟩ is a function h, then ⟨Maybe a⟩ simplfies to: mmap h, defined by:
mmap h Nothing = Nothing
mmap h (Just x) = Just (h x)
(Idea: mmap is like list’s map but for Maybe.)

Exercises

If e :: ∀a. (Integer → a) → Maybe a, then for all type A: for all f :: A → Integer : mmap f (e id) = e f

Meaning: e must hide a fixed secret Maybe Integer and apply f to the element (if any); the secret can be discovered by e id.

If e :: ∀a. Maybe a, then for all type A: for all x::A : mmap (const x) e() = eA
Further deduce that e should never be Just something, not even for just one particular type A. (This is similar to the (∀a. a) exercise.)

If e :: ∀a,b. a → Maybe b, then for all types A,B: for all x::A, y::B : mmap (const y) (e ()) = e x
Further deduce that e x should never be Just something. (Similar to the previous one.)

If u :: ∀a. a → Maybe a, then for all types A,B: for all h :: A → B, x :: A : mmap h (u x) = u (h x)

Meaning: See the next one.

If f :: ∀a. Maybe a → Maybe a, then for all types A,B: for all h :: A → B, xm :: Maybe A : mmap h (f xm) = f (mmap h xm)

Meaning: To appreciate the meaning of the above two, you should write all possibilities (2 for u, 2 for f). You will notice that in all cases, if the output is a Just with an element, it must come straight from the input, so applying h to the element (if any) before or after doesn’t matter.

(Advanced students go on to recognize: In category theory, f is a natural transformation from Maybe to Maybe, and u is a natural transformation from identity to Maybe.)

You can furthermore deduce that u and f have only the possibilities you wrote. (Hint: u () has only 2 possibilities, and plugging each into the proved equation gives the general case. Likewise for f with f Nothing and f (Just ()).)

In addition, we can prove that a polymorphic inverse of u—a generic way to “extract a from Maybe a”—should be impossible:

If e :: ∀a. Maybe a → a, then for all types A,B: for all h :: A → B, xm :: Maybe A : h (e xm) = e (mmap h xm)
Further deduce that e should not exist. (Hint: Apply to e() Nothing, then it is similar to ∀a. a.)

Eithers

Definition of ⟨Either a b⟩

Idea: xe ⟨Either a b⟩ ye iff: both are Left or both are Right, and the pair of elements are related by ⟨a⟩ or by ⟨b⟩ as appropriate.

Rigorously: ⟨Either a b⟩ is the smallest relation such that:

Pragmatically: If ⟨a⟩ is a function f and ⟨b⟩ is a function g, then ⟨Either a b⟩ simplifies to: emap f g, defined by:
emap f g (Left x) = Left (f x)
emap f g (Right u) = Right (g u)

Exercises

If e :: ∀a. a → Either a a, then for all type A: for all x :: A : emap (const x) (const x) (e ()) = e x

Meaning: If e () = Left (), then e = Left generally. If e () = Right (), then e = Right generally. So e is always Left or always Right.

If e :: ∀a,b. a → b → Either a b, then for all types A,B: for all x::A, y::B : emap (const x) (const y) (e () ()) = e x y

Meaning: Similar to the previous one.

Lists

Definition of ⟨[a]⟩

Idea: xs ⟨[a]⟩ ys iff both lists have the same length and every pair of elements is related by ⟨a⟩.

Rigorously (assuming finite lists): ⟨[a]⟩ is the smallest relation such that:

Pragmatically: If ⟨a⟩ is a function h, then ⟨[a]⟩ simplifies to: map h.

Exercises

If e :: ∀a. (Integer → a) → [a], then for all type A: for all f :: Integer → A : map f (e id) = e f

Meaning: e must hide a fixed secret [Integer] and apply f to each element; the secret can be discovered by e id.

If e :: ∀a. [a], then for all type A: for all x::A : map (const x) e() = eA
Further deduce that e should never be a non-empty list, not even for just one particular type A. (Similar to the (∀a. a) exercise.)

If e :: ∀a,b. a → [b], then for all types A,B: for all x::A, y::B : map (const y) (e ()) = e x
Further deduce that e x should never be a non-empty list. (Similar to the previous one.)

If e :: ∀a. [a] → [a], then for all types A,B: for all h :: A → B, xs :: [A] : map h (e xs) = e (map h xs)

Meaning: To appreciate the meaning, you should first gain some experience from creatively making a lot of different functions of that type. (Some examples: reverse, take 4, drop 3.) You will notice that you only have freedom to: restructure the list (e.g., change order, change length), drop elements, duplicate elements; but any element you put in the output list must come straight from the input list. Therefore, applying f to the elements before or after doesn’t matter.

In category theory, one may say “e is a natural transformation from List to List”.

There are also “from List to Maybe”, “from Maybe to List”, and further combinations!

If e :: ∀a. a → [a], then for all types A,B: for all h :: A → B, x :: A : map h (e x) = e (h x)

If e :: ∀a. [a] → a, then for all types A,B: for all h :: A → B, xs :: [A] : h (e xs) = e (map h xs)
Further deduce that e should not exist. (Similar to ∀a. Maybe a → a.) Fortunately, the following exists:

If e :: ∀a. [a] → Maybe a, then for all types A,B: for all h :: A → B, xs :: [A] : mmap h (e xs) = e (map h xs)
Further deduce e [] = Nothing.

If e :: ∀a. Maybe a → [a], then for all types A,B: for all h :: A → B, xm :: Maybe A : map h (e xm) = e (mmap h xm)

If e :: ∀a. [a] → [a] → [a], then for all types A,B: for all h :: A → B, xs1, xs2 :: [A] : map h (e xs1 xs2) = e (map h xs1) (map h xs2)

If e :: ∀a. [[a]] → [a], then for all types A,B : for all h :: A → B, xss :: [[A]] : map h (e xss) = e (map (map h) xss)

If e :: ∀a. [Maybe a] → [a], then for all types A,B: for all h :: A → B, xms :: [Maybe A] : map h (e xms) = e (map (mmap h) xms)

If e :: ∀a,c. [Either a c] → [a], then for all types A,B,C,C’: for all h :: A → B, k :: C → C’, xes :: [Either A C] : map h (e xes) = e (map (emap h k) xes)

If e :: ∀a,c. [Either c a] → [a], then for all types A,B,C,C’: for all h :: A → B, k :: C → C’, xes :: [Either C A] : map h (e xes) = e (map (emap k h) xes)

Encoding Algebraic Data Types by Polymorphism

To appreciate what the cryptic title means, go through the exercises in this section and try to spot a pattern of how an algebraic data type can be represented by a polymorphic type. :)

Bools

If e :: ∀a. a → a → a, then for all type A: for all f,t :: A : (if e False True then t else f) = e f t

Meaning: e can have only two possibilities (determined by and discoverable from e False True): Always output the 1st argument, or always output the 2nd argument.

Unit

If e :: ∀a. a → a, then for all type A: for all x :: A : x = e x

Meaning: e has only one possibility (the polymorphic identity function), so it is like ().

Tuples

We will need the following helpers:

mkTuple x y = (x,y)
foldTuple f (x,y) = f x y

If e :: ∀a. (Integer → String → a) → a, then for all type A: for all f :: Integer → String → A : foldTuple f (e mkTuple) = e f

Meaning: e can only apply f to a fixed secret pair of integer and string; the pair is discoverable by e fst and e snd.

Eithers

We will need the following helper, like list’s foldr but for Either:

foldEither f g (Left x) = f x
foldEither f g (Right y) = g y

If e :: ∀a. (Integer → a) → (String → a) → a, then for all type A: for all f :: Integer → a, g :: String → a : foldEither f g (e Left Right) = e f g

Meaning: e can only hide a fixed secret integer or string, and apply f to the integer or g to the string accordingly; e Left Right discovers the secret.

Naturals

We will need the following fold function for natural numbers:

foldNat z s 0 = z
foldNat z s (n+1) = s (foldNat z s n)
(Idea: apply s to z for n times; also analogous to list’s foldr.)

If e :: ∀a. a→(a→a)→a, then for all type A: for all z :: A, s :: A → A : foldNat z s (e 0 succ) = e z s
(where succ = λn. n+1)

Meaning: e can only apply s to z for a fixed secret natural number of times; the secret number can be discovered by e 0 succ. Possibilities of e correspond exactly to possibilities of natural numbers.

Maybes

We will need this helper, like list’s foldr but for Maybe:

foldMaybe z f Nothing = z
foldMaybe z f (Just x) = f x

If e :: ∀a. a → (Integer → a) → a, then for all type A: for all z :: A, f :: Integer → a : foldMaybe z f (e Nothing Just) = e z f

Meaning: e can only hide a fixed secret Nothing or Just integer, and output z or apply f to the integer accordingly; the secret can be discovered by e Nothing Just.

Lists

If e :: ∀a. (Integer → a → a) → a → a, then for all type A: for all op :: Integer → A → A, z :: A : foldr op z (e (:) []) = e op z

Meaning: e can only perform foldr on a fixed secret list of integers; the secret list can be discovered by e (:) [].

Binary Trees

We will need the following binary tree type (with integer elements) and a fold function analogous to list’s foldr:

data BTZ = Nil | Bin BTZ Integer BTZ

foldBTZ :: (a → Integer → a → a) → a → BTZ → a
foldBTZ op z Nil = z
foldBTZ op z (Bin lt x rt) = op (foldBTZ op z lt) x (foldBTZ op z rt)

If e :: ∀a. (a → Integer → a → a) → a → a, then for all type A: for all op :: A → Integer → A → A, z :: A : foldBTZ op z (e Bin Nil) = e op z

Meaning: e can only perform foldBTZ on a fixed secret tree; the secret tree can be discovered by e Bin Nil.