W-types were introduced by Martin-Löf and they serve as general framework for inductive
types in type theory. In this file, we show how several well-known inductive types can be
obtained via W-types. The types that we consider, are natural numbers, lists, and trees.
W-types only give us a suitable framework for inductive types if our type theory supports
an additional axiom, namely function extensionality. By default, Coq does not have function
extensionality built in, and because of that, inductive families are more suitable. We
formulate function extensionality for dependent functions.
Axiom funext
: forall {X : Type}
{Y : X -> Type}
(f g : forall (x : X), Y x),
(forall (x : X), f x = g x)
->
f = g.
To simplify the development, we also assume another axiom, which is called 'uniqueness of
identity proofs' (UIP). This axiom says that inhabitants of the identity type are unique. More
concretely, is says that every proof of equality is equal. UIP also is not available by default
in Coq.
Axiom UIP
: forall {X : Type}
{x y : X}
(p q : x = y),
p = q.
A W-type is specified by
- a type A that represents the names of the constructors;
- a type family B on A that represents the number of arguments of each constructor (i.e., the arity of each constructor).
Inductive W (A : Type) (B : A -> Type) : Type := | sup : forall (a : A), (B a -> W A B) -> W A B. Arguments sup {A B} _ _.
To understand W-types, let us consider the type of natural numbers. This type has two
constructors, namely zero and successor. The zero constructor takes no arguments, while
the successor takes only one argument. As such, for A we take the type of booleans,
because that type has two inhabitants. For B, we take the following type family.
Definition nat_arity : bool -> Type := fun b => match b with | true => False | false => unit end. Definition nat : Type := W bool nat_arity.
Since nat is a W-type, we only have the following constructor.
We can further specialize this constructor, because we have two cases for b. The first
possibility is that b is true, which gives us
Since there is a unique map from the empty type to every other type, we get the constructor
for zero.
natnatFalse -> W bool nat_arityinduction w. Defined.w: FalseW bool nat_arity
The other possibility is that b is false, and that gives us
From this we get the successor.
n: natnatn: natnatexact (fun _ => n). Defined.n: natunit -> W bool nat_arity
All in all, we see that for each inhabitant of A the W-type has a constructor with the
specified arity.
Now we look at the induction principle, and this is where we need to use axioms that we
assumed. A naive attempt to define the induction principle fails.
P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
n: natP nP: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
n: natP nP: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
a: bool
f: nat_arity a -> W bool nat_arity
H: forall b : nat_arity a, P (f b)P (sup a f)P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
f: False -> W bool nat_arity
H: forall b : False, P (f b)P (sup true f)P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
f: unit -> W bool nat_arity
H: forall b : unit, P (f b)P (sup false f)P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
f: False -> W bool nat_arity
H: forall b : False, P (f b)P (sup true f)P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
f: False -> W bool nat_arity
H: forall b : False, P (f b)P (sup true f)
This tactic fails. We need to give an inhabitant of P (sup true f), but the term
Pz has type P zero. Note that sup true f and zero are propositionally equal,
but not definitionally.
admit.P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
f: unit -> W bool nat_arity
H: forall b : unit, P (f b)P (sup false f)P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
f: unit -> W bool nat_arity
H: forall b : unit, P (f b)P (sup false f)
This tactic also fails. We need to give an inhabitant of type P (sup false f). However,
Ps (f tt) (H tt) has type P (suc (f tt)). Note that sup false f and suc (f tt)
are propositionally equal, but not definitionally.
admit. Abort.
To define the induction principle, we first define the transport operation. If we have a type
family Y over X and an equality x1 = x2, then every inhabitant of type Y x1 gives rise
to an inhabitant of type Y x2.
X: Type
Y: X -> Type
x1, x2: X
p: x1 = x2
y: Y x1Y x2X: Type
Y: X -> Type
x1, x2: X
p: x1 = x2
y: Y x1Y x2exact y. Defined.X: Type
Y: X -> Type
x2: X
y: Y x2Y x2
Now we prove the aforementioned equalities. Note the usage of function extensionality.
f: False -> natzero = sup true ff: False -> natzero = sup true ff: False -> natsup true (fun w : False => False_rect (W bool nat_arity) w) = sup true ff: False -> nat(fun w : False => False_rect (W bool nat_arity) w) = ff: False -> natforall x : False, False_rect (W bool nat_arity) x = f xinduction x. Qed.f: False -> nat
x: FalseFalse_rect (W bool nat_arity) x = f xf: unit -> natsuc (f tt) = sup false ff: unit -> natsuc (f tt) = sup false ff: unit -> natsup false (fun _ : unit => f tt) = sup false ff: unit -> nat(fun _ : unit => f tt) = ff: unit -> natforall x : unit, f tt = f xf: unit -> nat
x: unitf tt = f xreflexivity. Qed.f: unit -> natf tt = f tt
And now we have everything in place to prove the induction principle for the natural
numbers.
Section Induction. Context {P : nat -> Type} (Pz : P zero) (Ps : forall (n : nat), P n -> P (suc n)).P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
n: natP nP: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
n: natP nP: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
a: bool
f: nat_arity a -> W bool nat_arity
H: forall b : nat_arity a, P (f b)P (sup a f)P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
f: False -> W bool nat_arity
H: forall b : False, P (f b)P (sup true f)P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
f: unit -> W bool nat_arity
H: forall b : unit, P (f b)P (sup false f)P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
f: False -> W bool nat_arity
H: forall b : False, P (f b)P (sup true f)apply nat_ind_z_eq.P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
f: False -> W bool nat_arity
H: forall b : False, P (f b)zero = sup true fP: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
f: unit -> W bool nat_arity
H: forall b : unit, P (f b)P (sup false f)apply nat_ind_s_eq. Defined.P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
f: unit -> W bool nat_arity
H: forall b : unit, P (f b)suc (f tt) = sup false f
3.5. Computation rules
The key thing to note is that nat_ind zero evaluates to transport P nat_ind_z_eq Pz,
and we want to prove this is equal to pz. Here we have nat_ind_z_eq : zero = zero,
so we can use the following lemma to simplify this term.
P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
X: Type
Y: X -> Type
x: X
p: x = x
y: Y xtransport Y p y = yP: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
X: Type
Y: X -> Type
x: X
p: x = x
y: Y xtransport Y p y = yP: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
X: Type
Y: X -> Type
x: X
p: x = x
y: Y xp = eq_reflP: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
X: Type
Y: X -> Type
x: X
p: x = x
y: Y x
r: p = eq_refltransport Y p y = yapply UIP.P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
X: Type
Y: X -> Type
x: X
p: x = x
y: Y xp = eq_reflP: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
X: Type
Y: X -> Type
x: X
p: x = x
y: Y x
r: p = eq_refltransport Y p y = yreflexivity. Qed.P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
X: Type
Y: X -> Type
x: X
p: x = x
y: Y x
r: p = eq_refltransport Y eq_refl y = y
Now we have everything to prove the necessary computation rules. They are proven in the
same way for both zero and the successor.
P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)nat_ind zero = PzP: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)nat_ind zero = PzP: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)transport P nat_ind_z_eq Pz = Pzreflexivity. Qed.P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)Pz = PzP: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
n: natnat_ind (suc n) = Ps n (nat_ind n)P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
n: natnat_ind (suc n) = Ps n (nat_ind n)P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
n: nattransport P nat_ind_s_eq (Ps n (nat_ind n)) = Ps n (nat_ind n)reflexivity. Qed. End Induction.P: nat -> Type
Pz: P zero
Ps: forall n : nat, P n -> P (suc n)
n: natPs n (nat_ind n) = Ps n (nat_ind n)
All in all, we have shown that the natural numbers can be constructed as a W-type, and
that we can derive the usual constructors and induction rule for them. In this construction,
we used function extensionality, which is why W-types only work well if we have that axiom.
Since Coq does not support function extensionality by default, W-types do not give a good way
to develop inductive definitions. However, there are other forms of type theory where function
extensionality is available and for those W-types are suitable.
Section FixAType. Context (X : Type).
5.1. The type
cons : X → list → list
Definition list_arg (x : unit + X) : Type := match x with | inl x => False | inr a => unit end. Definition list : Type := W (unit + X) list_arg.
5.2. The constructors
X: TypelistX: TypelistX: Typelist_arg (inl tt) -> W (unit + X) list_argX: TypeFalse -> W (unit + X) list_arginduction x. Defined.X: Type
x: FalseW (unit + X) list_argX: Type
x: X
xs: listlistX: Type
x: X
xs: listlistX: Type
x: X
xs: listlist_arg (inr x) -> W (unit + X) list_argexact (fun _ => xs). Defined.X: Type
x: X
xs: listunit -> W (unit + X) list_arg
5.3. The induction principle
X: Type
f: False -> W (unit + X) list_argnil = sup (inl tt) fX: Type
f: False -> W (unit + X) list_argnil = sup (inl tt) fX: Type
f: False -> W (unit + X) list_argsup (inl tt) (fun x : False => False_rect (W (unit + X) list_arg) x) = sup (inl tt) fX: Type
f: False -> W (unit + X) list_arg(fun x : False => False_rect (W (unit + X) list_arg) x) = fX: Type
f: False -> W (unit + X) list_argforall x : False, False_rect (W (unit + X) list_arg) x = f xinduction x. Qed.X: Type
f: False -> W (unit + X) list_arg
x: FalseFalse_rect (W (unit + X) list_arg) x = f xX: Type
x: X
f: unit -> listcons x (f tt) = sup (inr x) fX: Type
x: X
f: unit -> listcons x (f tt) = sup (inr x) fX: Type
x: X
f: unit -> listsup (inr x) (fun _ : unit => f tt) = sup (inr x) fX: Type
x: X
f: unit -> list(fun _ : unit => f tt) = fX: Type
x: X
f: unit -> listforall x : unit, f tt = f xX: Type
x: X
f: unit -> list
z: unitf tt = f zreflexivity. Qed. Section Induction. Context {P : list -> Type} (Pnil : P nil) (Pcons : forall (x : X) (xs : list), P xs -> P (cons x xs)).X: Type
x: X
f: unit -> listf tt = f ttX: Type
P: list -> Type
Pnil: P nil
Pcons: forall (x : X) (xs : list), P xs -> P (cons x xs)
xs: listP xsX: Type
P: list -> Type
Pnil: P nil
Pcons: forall (x : X) (xs : list), P xs -> P (cons x xs)
xs: listP xsX: Type
P: list -> Type
Pnil: P nil
Pcons: forall (x : X) (xs : list), P xs -> P (cons x xs)
a: (unit + X)%type
f: list_arg a -> W (unit + X) list_arg
H: forall b : list_arg a, P (f b)P (sup a f)X: Type
P: list -> Type
Pnil: P nil
Pcons: forall (x : X) (xs : list), P xs -> P (cons x xs)
f: False -> W (unit + X) list_arg
H: forall b : False, P (f b)P (sup (inl tt) f)X: Type
P: list -> Type
Pnil: P nil
Pcons: forall (x : X) (xs : list), P xs -> P (cons x xs)
x: X
f: unit -> W (unit + X) list_arg
H: forall b : unit, P (f b)P (sup (inr x) f)X: Type
P: list -> Type
Pnil: P nil
Pcons: forall (x : X) (xs : list), P xs -> P (cons x xs)
f: False -> W (unit + X) list_arg
H: forall b : False, P (f b)P (sup (inl tt) f)apply list_nil_eq.X: Type
P: list -> Type
Pnil: P nil
Pcons: forall (x : X) (xs : list), P xs -> P (cons x xs)
f: False -> W (unit + X) list_arg
H: forall b : False, P (f b)nil = sup (inl tt) fX: Type
P: list -> Type
Pnil: P nil
Pcons: forall (x : X) (xs : list), P xs -> P (cons x xs)
x: X
f: unit -> W (unit + X) list_arg
H: forall b : unit, P (f b)P (sup (inr x) f)apply list_cons_eq. Defined.X: Type
P: list -> Type
Pnil: P nil
Pcons: forall (x : X) (xs : list), P xs -> P (cons x xs)
x: X
f: unit -> W (unit + X) list_arg
H: forall b : unit, P (f b)cons x (f tt) = sup (inr x) f
X: Type
P: list -> Type
Pnil: P nil
Pcons: forall (x : X) (xs : list), P xs -> P (cons x xs)list_ind nil = PnilX: Type
P: list -> Type
Pnil: P nil
Pcons: forall (x : X) (xs : list), P xs -> P (cons x xs)list_ind nil = PnilX: Type
P: list -> Type
Pnil: P nil
Pcons: forall (x : X) (xs : list), P xs -> P (cons x xs)transport P list_nil_eq Pnil = Pnilreflexivity. Qed.X: Type
P: list -> Type
Pnil: P nil
Pcons: forall (x : X) (xs : list), P xs -> P (cons x xs)Pnil = PnilX: Type
P: list -> Type
Pnil: P nil
Pcons: forall (x : X) (xs : list), P xs -> P (cons x xs)
x: X
xs: listlist_ind (cons x xs) = Pcons x xs (list_ind xs)X: Type
P: list -> Type
Pnil: P nil
Pcons: forall (x : X) (xs : list), P xs -> P (cons x xs)
x: X
xs: listlist_ind (cons x xs) = Pcons x xs (list_ind xs)X: Type
P: list -> Type
Pnil: P nil
Pcons: forall (x : X) (xs : list), P xs -> P (cons x xs)
x: X
xs: listtransport P list_cons_eq (Pcons x xs (list_ind xs)) = Pcons x xs (list_ind xs)reflexivity. Qed. End Induction. End FixAType.X: Type
P: list -> Type
Pnil: P nil
Pcons: forall (x : X) (xs : list), P xs -> P (cons x xs)
x: X
xs: listPcons x xs (list_ind xs) = Pcons x xs (list_ind xs)
Section FixAType. Context (X : Type).
6.1. The type
Definition tree_arg (x : unit + X) : Type := match x with | inl x => False | inr a => bool end. Definition tree : Type := W (unit + X) tree_arg.
6.2. The constructors
X: TypetreeX: TypetreeX: Typetree_arg (inl tt) -> W (unit + X) tree_argX: TypeFalse -> W (unit + X) tree_arginduction x. Defined.X: Type
x: FalseW (unit + X) tree_argX: Type
x: X
l, r: treetreeX: Type
x: X
l, r: treetreeX: Type
x: X
l, r: treetree_arg (inr x) -> W (unit + X) tree_argX: Type
x: X
l, r: treebool -> W (unit + X) tree_argX: Type
x: X
l, r: tree
b: boolW (unit + X) tree_argX: Type
x: X
l, r: treeW (unit + X) tree_argX: Type
x: X
l, r: treeW (unit + X) tree_argexact l.X: Type
x: X
l, r: treeW (unit + X) tree_argexact r. Defined.X: Type
x: X
l, r: treeW (unit + X) tree_arg
6.3. The induction principle
X: Type
f: False -> W (unit + X) tree_argleaf = sup (inl tt) fX: Type
f: False -> W (unit + X) tree_argleaf = sup (inl tt) fX: Type
f: False -> W (unit + X) tree_argsup (inl tt) (fun x : False => False_rect (W (unit + X) tree_arg) x) = sup (inl tt) fX: Type
f: False -> W (unit + X) tree_arg(fun x : False => False_rect (W (unit + X) tree_arg) x) = fX: Type
f: False -> W (unit + X) tree_argforall x : False, False_rect (W (unit + X) tree_arg) x = f xinduction x. Qed.X: Type
f: False -> W (unit + X) tree_arg
x: FalseFalse_rect (W (unit + X) tree_arg) x = f xX: Type
x: X
f: bool -> treenode x (f true) (f false) = sup (inr x) fX: Type
x: X
f: bool -> treenode x (f true) (f false) = sup (inr x) fX: Type
x: X
f: bool -> treesup (inr x) (fun b : bool => bool_rect (fun _ : bool => W (unit + X) tree_arg) (f true) (f false) b) = sup (inr x) fX: Type
x: X
f: bool -> tree(fun b : bool => bool_rect (fun _ : bool => W (unit + X) tree_arg) (f true) (f false) b) = fX: Type
x: X
f: bool -> treeforall x : bool, bool_rect (fun _ : bool => W (unit + X) tree_arg) (f true) (f false) x = f xX: Type
x: X
f: bool -> tree
b: boolbool_rect (fun _ : bool => W (unit + X) tree_arg) (f true) (f false) b = f bX: Type
x: X
f: bool -> treebool_rect (fun _ : bool => W (unit + X) tree_arg) (f true) (f false) true = f trueX: Type
x: X
f: bool -> treebool_rect (fun _ : bool => W (unit + X) tree_arg) (f true) (f false) false = f falsereflexivity.X: Type
x: X
f: bool -> treebool_rect (fun _ : bool => W (unit + X) tree_arg) (f true) (f false) true = f truereflexivity. Qed. Section Induction. Context {P : tree -> Type} (Pleaf : P leaf) (Pnode : forall (x : X) (l r : tree), P l -> P r -> P (node x l r)).X: Type
x: X
f: bool -> treebool_rect (fun _ : bool => W (unit + X) tree_arg) (f true) (f false) false = f falseX: Type
P: tree -> Type
Pleaf: P leaf
Pnode: forall (x : X) (l r : tree), P l -> P r -> P (node x l r)
t: treeP tX: Type
P: tree -> Type
Pleaf: P leaf
Pnode: forall (x : X) (l r : tree), P l -> P r -> P (node x l r)
t: treeP tX: Type
P: tree -> Type
Pleaf: P leaf
Pnode: forall (x : X) (l r : tree), P l -> P r -> P (node x l r)
a: (unit + X)%type
f: tree_arg a -> W (unit + X) tree_arg
H: forall b : tree_arg a, P (f b)P (sup a f)X: Type
P: tree -> Type
Pleaf: P leaf
Pnode: forall (x : X) (l r : tree), P l -> P r -> P (node x l r)
f: False -> W (unit + X) tree_arg
H: forall b : False, P (f b)P (sup (inl tt) f)X: Type
P: tree -> Type
Pleaf: P leaf
Pnode: forall (x : X) (l r : tree), P l -> P r -> P (node x l r)
x: X
f: bool -> W (unit + X) tree_arg
H: forall b : bool, P (f b)P (sup (inr x) f)X: Type
P: tree -> Type
Pleaf: P leaf
Pnode: forall (x : X) (l r : tree), P l -> P r -> P (node x l r)
f: False -> W (unit + X) tree_arg
H: forall b : False, P (f b)P (sup (inl tt) f)apply tree_ind_leaf_eq.X: Type
P: tree -> Type
Pleaf: P leaf
Pnode: forall (x : X) (l r : tree), P l -> P r -> P (node x l r)
f: False -> W (unit + X) tree_arg
H: forall b : False, P (f b)leaf = sup (inl tt) fX: Type
P: tree -> Type
Pleaf: P leaf
Pnode: forall (x : X) (l r : tree), P l -> P r -> P (node x l r)
x: X
f: bool -> W (unit + X) tree_arg
H: forall b : bool, P (f b)P (sup (inr x) f)apply tree_ind_node_eq. Defined.X: Type
P: tree -> Type
Pleaf: P leaf
Pnode: forall (x : X) (l r : tree), P l -> P r -> P (node x l r)
x: X
f: bool -> W (unit + X) tree_arg
H: forall b : bool, P (f b)node x (f true) (f false) = sup (inr x) f
X: Type
P: tree -> Type
Pleaf: P leaf
Pnode: forall (x : X) (l r : tree), P l -> P r -> P (node x l r)tree_ind leaf = PleafX: Type
P: tree -> Type
Pleaf: P leaf
Pnode: forall (x : X) (l r : tree), P l -> P r -> P (node x l r)tree_ind leaf = PleafX: Type
P: tree -> Type
Pleaf: P leaf
Pnode: forall (x : X) (l r : tree), P l -> P r -> P (node x l r)transport P tree_ind_leaf_eq Pleaf = Pleafreflexivity. Qed.X: Type
P: tree -> Type
Pleaf: P leaf
Pnode: forall (x : X) (l r : tree), P l -> P r -> P (node x l r)Pleaf = PleafX: Type
P: tree -> Type
Pleaf: P leaf
Pnode: forall (x : X) (l r : tree), P l -> P r -> P (node x l r)
x: X
l, r: treetree_ind (node x l r) = Pnode x l r (tree_ind l) (tree_ind r)X: Type
P: tree -> Type
Pleaf: P leaf
Pnode: forall (x : X) (l r : tree), P l -> P r -> P (node x l r)
x: X
l, r: treetree_ind (node x l r) = Pnode x l r (tree_ind l) (tree_ind r)X: Type
P: tree -> Type
Pleaf: P leaf
Pnode: forall (x : X) (l r : tree), P l -> P r -> P (node x l r)
x: X
l, r: treetransport P tree_ind_node_eq (Pnode x l r (tree_ind l) (tree_ind r)) = Pnode x l r (tree_ind l) (tree_ind r)reflexivity. Qed. End Induction. End FixAType.X: Type
P: tree -> Type
Pleaf: P leaf
Pnode: forall (x : X) (l r : tree), P l -> P r -> P (node x l r)
x: X
l, r: treePnode x l r (tree_ind l) (tree_ind r) = Pnode x l r (tree_ind l) (tree_ind r)
Construct the following inductive type using W-types.
Inductive Expr : Type :=
| num : nat -> Expr
| plus : Expr -> Expr -> Expr.
W-types can be used to define various inductive types, as long as the type theory that is
used supports function extensionality. This holds for a wide class of data types, namely
those given by polynomial functors.
- Constructive mathematics and computer programming, Per Martin-Löf
- Intuitionistic type theory, Per Martin-Löf
- Inductive families, Peter Dybjer
- Representing inductively defined sets by wellorderings in Martin-Löf's type theory, Peter Dybjer