Exercise session 3 — Solutions
🏠 Go back to the course homepage.
From Stdlib Require Import List. Import ListNotations. Set Default Goal Selector "!".
EXERCISE
Prove the following statements using proof terms. If it's too hard to do directly, you can use the interactive mode together with the refine tactic.
We give you this type for "lower than" (lt).
EXERCISE
Prove the following lemma interactively using intros and apply.
forall n m : nat, lt n m -> lt n (4 + m)forall n m : nat, lt n m -> lt n (4 + m)n, m: nat
h: lt n mlt n (4 + m)n, m: nat
h: lt n mlt n (S (S (S m)))n, m: nat
h: lt n mlt n (S (S m))n, m: nat
h: lt n mlt n (S m)apply h.n, m: nat
h: lt n mlt n m
Note we make lt_S easier to use by making n, m implicit.
Arguments lt_S {n m}.EXERCISE
Prove the following with a proof term.
EXERCISE
Prove the following with a proof term. Hint: Use Print "\/" if needed.
EXERCISE
Prove the following with a proof term.
We can also write it this way:
EXERCISE
If you have trouble with this one, try to use refine to fill the term interactively.
Impredicative encodings
Thanks to impredicativity of Prop (the ability to quantify over propositions within propositions) it is possible to define most connectives using only forall and -> (no inductive definitions).
For instance, one can define False as follows:
EXERCISE
Show that iFalse is equivalent to False.
False <-> iFalseFalse <-> iFalseFalse -> iFalseiFalse -> Falsecontradiction.False -> iFalseiFalse -> Falseapply h.h: iFalseFalse
EXERCISE
Define the impredicative encoding of True and show it equivalent to True.
True <-> iTrueTrue <-> iTrueTrue -> iTrueiTrue -> TrueTrue -> iTrueiTrueassumption.h: iFalseiFalseiTrue -> Trueconstructor.True
EXERCISE
Now do the same thing for conjunction, disjunction and the existential quantifier.
If you look at the solution you should see that they are in a sense connected to the elimination rule of the corresponding connective. Note that you can get it basically by using Print and_ind.
For conjunction, the idea is that having a proof of A /\ B is that same as being able to prove any X as long as A -> B -> X holds, in other words as long as X is provable assuming A and B.
Another way of seeing it is that if you have h : iAnd P Q then apply h should be equivalent to destruct h' with h' : P /\ Q.
forall P Q : Prop, P /\ Q <-> iAnd P Qforall P Q : Prop, P /\ Q <-> iAnd P QP, Q: PropP /\ Q <-> iAnd P QP, Q: PropP /\ Q -> iAnd P QP, Q: PropiAnd P Q -> P /\ QP, Q: PropP /\ Q -> iAnd P QP, Q: Prop
hP: P
hQ: QiAnd P QP, Q: Prop
hP: P
hQ: Q
X: Prop
h: P -> Q -> XXall: assumption.P, Q: Prop
hP: P
hQ: Q
X: Prop
h: P -> Q -> XPP, Q: Prop
hP: P
hQ: Q
X: Prop
h: P -> Q -> XQP, Q: PropiAnd P Q -> P /\ QP, Q: Prop
h: iAnd P QP /\ QP, Q: Prop
h: iAnd P QP -> Q -> P /\ QP, Q: Prop
h: iAnd P Q
hP: P
hQ: QP /\ Qall: assumption.P, Q: Prop
h: iAnd P Q
hP: P
hQ: QPP, Q: Prop
h: iAnd P Q
hP: P
hQ: QQ
For disjunction it's similar, but with two branches.
forall P Q : Prop, P \/ Q <-> iOr P Qforall P Q : Prop, P \/ Q <-> iOr P QP, Q: PropP \/ Q <-> iOr P QP, Q: PropP \/ Q -> iOr P QP, Q: PropiOr P Q -> P \/ QP, Q: PropP \/ Q -> iOr P QP, Q: Prop
h: P \/ Q
X: Prop
hP: P -> X
hQ: Q -> XXP, Q: Prop
H: P
X: Prop
hP: P -> X
hQ: Q -> XXP, Q: Prop
H: Q
X: Prop
hP: P -> X
hQ: Q -> XXP, Q: Prop
H: P
X: Prop
hP: P -> X
hQ: Q -> XXassumption.P, Q: Prop
H: P
X: Prop
hP: P -> X
hQ: Q -> XPP, Q: Prop
H: Q
X: Prop
hP: P -> X
hQ: Q -> XXassumption.P, Q: Prop
H: Q
X: Prop
hP: P -> X
hQ: Q -> XQP, Q: PropiOr P Q -> P \/ QP, Q: Prop
h: iOr P QP \/ QP, Q: Prop
h: iOr P QP -> P \/ QP, Q: Prop
h: iOr P QQ -> P \/ QP, Q: Prop
h: iOr P QP -> P \/ QP, Q: Prop
h: iOr P Q
H: PP \/ Qassumption.P, Q: Prop
h: iOr P Q
H: PPP, Q: Prop
h: iOr P QQ -> P \/ QP, Q: Prop
h: iOr P Q
H: QP \/ Qassumption.P, Q: Prop
h: iOr P Q
H: QQforall (X : Type) (P : X -> Prop), (exists x : X, P x) <-> iEx X Pforall (X : Type) (P : X -> Prop), (exists x : X, P x) <-> iEx X PX: Type
P: X -> Prop(exists x : X, P x) <-> iEx X PX: Type
P: X -> Prop(exists x : X, P x) -> iEx X PX: Type
P: X -> PropiEx X P -> exists x : X, P xX: Type
P: X -> Prop(exists x : X, P x) -> iEx X PX: Type
P: X -> Prop
x: X
hx: P x
Y: Prop
h:= forall x: X, P x -> YYassumption.X: Type
P: X -> Prop
x: X
hx: P x
Y: Prop
h: YYX: Type
P: X -> PropiEx X P -> exists x : X, P xX: Type
P: X -> Prop
h: iEx X Pexists x : X, P xX: Type
P: X -> Prop
h: iEx X Pforall x : X, P x -> exists x0 : X, P x0X: Type
P: X -> Prop
h: iEx X P
x: X
hx: P xexists x0 : X, P x0assumption.X: Type
P: X -> Prop
h: iEx X P
x: X
hx: P xP x
Advanced exercises
Mutual inductive types
It is also possible to define several inductive types at the same time. You just combine them with the with keyword.
This way we can define the type of trees mutually with that of forests (which are basically lists of trees).
You can then define mutual definitions over such types by using Fixpoint and the with keyword.
EXERCISE
Define a map function for ntree. Hint: You probably will have to change Definition into something else. Answer: You need Fixpoint.
Nested inductive types
Finally, you can define more inductive types by using what is called nesting. In the type below, you define a tree as something that contains a list of trees.
EXERCISE
Can you give an element of type tree?
Arguments All_nil {X P}. Arguments All_cons {X P}.
EXERCISE
Extend the type tree to have labelled nodes.
Prove a bijection between the new tree and ntree, ie. define functions to_tree : forall A, ntree A -> tree A from_tree : forall A, tree A -> ntree A and show that they invert each other.
For the solution, we will make a new tree type, called xtree for e``x``tended.
We reprove the induction principle from above.
Then we can use it to prove one direction.
forall (A : Type) (t : xtree A), to_tree (from_tree t) = tforall (A : Type) (t : xtree A), to_tree (from_tree t) = tA: Type
t: xtree Ato_tree (from_tree t) = tA: Type
a: A
ts: list (xtree A)
ih:= All (fun t: xtree A => to_tree (from_tree t) = t) tsto_tree (from_tree (Nx A a ts)) = Nx A a tsA: Type
a: A
ts: list (xtree A)
ih:= All (fun t: xtree A => to_tree (from_tree t) = t) tsNx A a (to_tree_list ((fix F (ts0 : list (xtree A)) : nforest A := match ts0 with | [] => nnil A | t :: l => ncons A (from_tree t) (F l) end) ts)) = Nx A a tsA: Type
a: A
ts: list (xtree A)
ih:= All (fun t: xtree A => to_tree (from_tree t) = t) tsto_tree_list ((fix F (ts0 : list (xtree A)) : nforest A := match ts0 with | [] => nnil A | t :: l => ncons A (from_tree t) (F l) end) ts) = tsA: Type
a: Ato_tree_list (nnil A) = []A: Type
a: A
t: xtree A
l: list (xtree A)
e: to_tree (from_tree t) = t
hl:= All (fun t: xtree A => to_tree (from_tree t) = t) l
ihl:= to_tree_list ((fix F (ts : list (xtree A)) : nforest A := match ts with | [] => nnil A | t :: l => ncons A (from_tree t) (F l) end) l) = lto_tree_list (ncons A (from_tree t) ((fix F (ts : list (xtree A)) : nforest A := match ts with | [] => nnil A | t0 :: l0 => ncons A (from_tree t0) (F l0) end) l)) = t :: lreflexivity.A: Type
a: Ato_tree_list (nnil A) = []A: Type
a: A
t: xtree A
l: list (xtree A)
e: to_tree (from_tree t) = t
hl:= All (fun t: xtree A => to_tree (from_tree t) = t) l
ihl:= to_tree_list ((fix F (ts : list (xtree A)) : nforest A := match ts with | [] => nnil A | t :: l => ncons A (from_tree t) (F l) end) l) = lto_tree_list (ncons A (from_tree t) ((fix F (ts : list (xtree A)) : nforest A := match ts with | [] => nnil A | t0 :: l0 => ncons A (from_tree t0) (F l0) end) l)) = t :: lA: Type
a: A
t: xtree A
l: list (xtree A)
e: to_tree (from_tree t) = t
hl:= All (fun t: xtree A => to_tree (from_tree t) = t) l
ihl:= to_tree_list ((fix F (ts : list (xtree A)) : nforest A := match ts with | [] => nnil A | t :: l => ncons A (from_tree t) (F l) end) l) = lto_tree (from_tree t) :: to_tree_list ((fix F (ts : list (xtree A)) : nforest A := match ts with | [] => nnil A | t0 :: l0 => ncons A (from_tree t0) (F l0) end) l) = t :: lA: Type
a: A
t: xtree A
l: list (xtree A)
e: to_tree (from_tree t) = t
hl:= All (fun t: xtree A => to_tree (from_tree t) = t) l
ihl:= to_tree_list ((fix F (ts : list (xtree A)) : nforest A := match ts with | [] => nnil A | t :: l => ncons A (from_tree t) (F l) end) l) = lt :: to_tree_list ((fix F (ts : list (xtree A)) : nforest A := match ts with | [] => nnil A | t0 :: l0 => ncons A (from_tree t0) (F l0) end) l) = t :: lapply ihl.A: Type
a: A
t: xtree A
l: list (xtree A)
e: to_tree (from_tree t) = t
hl:= All (fun t: xtree A => to_tree (from_tree t) = t) l
ihl:= to_tree_list ((fix F (ts : list (xtree A)) : nforest A := match ts with | [] => nnil A | t :: l => ncons A (from_tree t) (F l) end) l) = lto_tree_list ((fix F (ts : list (xtree A)) : nforest A := match ts with | [] => nnil A | t0 :: l0 => ncons A (from_tree t0) (F l0) end) l) = l
For the other direction, it's maybe more complicated.
We'll show you several ways. One is to use mutual fixed points like for when defining the function. When doing this interactively, we have to be careful that we indeed use the induction hypothesis only on strict subterms. Warning: Rocq will only check at Qed, not before. You can still ask if it's ok up to this point by using the Guarded command inside the proof.
For this, we also give a name to the fix nested in from_tree.
(* Fixpoint from_tree_list {A} (l : list (xtree A)) := match l with | [] => nnil _ | t :: l => ncons _ (from_tree t) (from_tree_list l) end. *)from_to_tree:= forall (A : Type) (t: ntree A), from_tree (to_tree t) = t
from_to_tree_list:= forall (A : Type) (l: nforest A), from_tree_list (to_tree_list l) = l
A: Type
t: ntree Afrom_tree (to_tree t) = tfrom_to_tree:= forall (A : Type) (t: ntree A), from_tree (to_tree t) = t
from_to_tree_list:= forall (A : Type) (l: nforest A), from_tree_list (to_tree_list l) = l
A: Type
l: nforest Afrom_tree_list (to_tree_list l) = lfrom_to_tree:= forall (A : Type) (t: ntree A), from_tree (to_tree t) = t
from_to_tree_list:= forall (A : Type) (l: nforest A), from_tree_list (to_tree_list l) = l
A: Type
t: ntree Afrom_tree (to_tree t) = tfrom_to_tree:= forall (A : Type) (t: ntree A), from_tree (to_tree t) = t
from_to_tree_list:= forall (A : Type) (l: nforest A), from_tree_list (to_tree_list l) = l
A: Type
l: nforest Afrom_tree_list (to_tree_list l) = lfrom_to_tree:= forall (A : Type) (t: ntree A), from_tree (to_tree t) = t
from_to_tree_list:= forall (A : Type) (l: nforest A), from_tree_list (to_tree_list l) = l
A: Type
t: ntree Afrom_tree (to_tree t) = tfrom_to_tree:= forall (A : Type) (t: ntree A), from_tree (to_tree t) = t
from_to_tree_list:= forall (A : Type) (l: nforest A), from_tree_list (to_tree_list l) = l
A: Type
a: A
l: nforest Afrom_tree (to_tree (nnode A a l)) = nnode A a lfrom_to_tree:= forall (A : Type) (t: ntree A), from_tree (to_tree t) = t
from_to_tree_list:= forall (A : Type) (l: nforest A), from_tree_list (to_tree_list l) = l
A: Type
a: A
l: nforest Annode A a ((fix F (ts : list (xtree A)) : nforest A := match ts with | [] => nnil A | t :: l0 => ncons A (from_tree t) (F l0) end) (to_tree_list l)) = nnode A a lapply from_to_tree_list.from_to_tree:= forall (A : Type) (t: ntree A), from_tree (to_tree t) = t
from_to_tree_list:= forall (A : Type) (l: nforest A), from_tree_list (to_tree_list l) = l
A: Type
a: A
l: nforest A(fix F (ts : list (xtree A)) : nforest A := match ts with | [] => nnil A | t :: l0 => ncons A (from_tree t) (F l0) end) (to_tree_list l) = lfrom_to_tree:= forall (A : Type) (t: ntree A), from_tree (to_tree t) = t
from_to_tree_list:= forall (A : Type) (l: nforest A), from_tree_list (to_tree_list l) = l
A: Type
l: nforest Afrom_tree_list (to_tree_list l) = lfrom_to_tree:= forall (A : Type) (t: ntree A), from_tree (to_tree t) = t
from_to_tree_list:= forall (A : Type) (l: nforest A), from_tree_list (to_tree_list l) = l
A: Typefrom_tree_list (to_tree_list (nnil A)) = nnil Afrom_to_tree:= forall (A : Type) (t: ntree A), from_tree (to_tree t) = t
from_to_tree_list:= forall (A : Type) (l: nforest A), from_tree_list (to_tree_list l) = l
A: Type
a: ntree A
l: nforest Afrom_tree_list (to_tree_list (ncons A a l)) = ncons A a lreflexivity.from_to_tree:= forall (A : Type) (t: ntree A), from_tree (to_tree t) = t
from_to_tree_list:= forall (A : Type) (l: nforest A), from_tree_list (to_tree_list l) = l
A: Typefrom_tree_list (to_tree_list (nnil A)) = nnil Afrom_to_tree:= forall (A : Type) (t: ntree A), from_tree (to_tree t) = t
from_to_tree_list:= forall (A : Type) (l: nforest A), from_tree_list (to_tree_list l) = l
A: Type
a: ntree A
l: nforest Afrom_tree_list (to_tree_list (ncons A a l)) = ncons A a lfrom_to_tree:= forall (A : Type) (t: ntree A), from_tree (to_tree t) = t
from_to_tree_list:= forall (A : Type) (l: nforest A), from_tree_list (to_tree_list l) = l
A: Type
a: ntree A
l: nforest Ancons A (from_tree (to_tree a)) (from_tree_list (to_tree_list l)) = ncons A a lreflexivity.from_to_tree:= forall (A : Type) (t: ntree A), from_tree (to_tree t) = t
from_to_tree_list:= forall (A : Type) (l: nforest A), from_tree_list (to_tree_list l) = l
A: Type
a: ntree A
l: nforest Ancons A a l = ncons A a l
Another option is to use Scheme which will derive better induction principles, working with mutual inductive types.
With them, no more fear of non-terminating fixed points.
forall (A : Type) (t : ntree A), from_tree (to_tree t) = tforall (A : Type) (t : ntree A), from_tree (to_tree t) = tA: Type
t: ntree Afrom_tree (to_tree t) = t
Before we apply the induction principle, we tell Rocq to see the goal as a predicate over t with the pattern t tactic.
A: Type
t: ntree A(fun n : ntree A => from_tree (to_tree n) = n) t
Now we need to give the predicate for forests:
A: Type
t: ntree Aforall (a : A) (n : nforest A), from_tree_list (to_tree_list n) = n -> from_tree (to_tree (nnode A a n)) = nnode A a nA: Type
t: ntree Afrom_tree_list (to_tree_list (nnil A)) = nnil AA: Type
t: ntree Aforall n : ntree A, from_tree (to_tree n) = n -> forall n0 : nforest A, from_tree_list (to_tree_list n0) = n0 -> from_tree_list (to_tree_list (ncons A n n0)) = ncons A n n0A: Type
t: ntree Aforall (a : A) (n : nforest A), from_tree_list (to_tree_list n) = n -> from_tree (to_tree (nnode A a n)) = nnode A a nA: Type
t: ntree A
a: A
l: nforest A
ih: from_tree_list (to_tree_list l) = lfrom_tree (to_tree (nnode A a l)) = nnode A a lA: Type
t: ntree A
a: A
l: nforest A
ih: from_tree_list (to_tree_list l) = lnnode A a ((fix F (ts : list (xtree A)) : nforest A := match ts with | [] => nnil A | t0 :: l0 => ncons A (from_tree t0) (F l0) end) (to_tree_list l)) = nnode A a lapply ih.A: Type
t: ntree A
a: A
l: nforest A
ih: from_tree_list (to_tree_list l) = l(fix F (ts : list (xtree A)) : nforest A := match ts with | [] => nnil A | t0 :: l0 => ncons A (from_tree t0) (F l0) end) (to_tree_list l) = lreflexivity.A: Type
t: ntree Afrom_tree_list (to_tree_list (nnil A)) = nnil AA: Type
t: ntree Aforall n : ntree A, from_tree (to_tree n) = n -> forall n0 : nforest A, from_tree_list (to_tree_list n0) = n0 -> from_tree_list (to_tree_list (ncons A n n0)) = ncons A n n0A: Type
t, t': ntree A
iht: from_tree (to_tree t') = t'
l: nforest A
ihl: from_tree_list (to_tree_list l) = lfrom_tree_list (to_tree_list (ncons A t' l)) = ncons A t' lA: Type
t, t': ntree A
iht: from_tree (to_tree t') = t'
l: nforest A
ihl: from_tree_list (to_tree_list l) = lncons A (from_tree (to_tree t')) (from_tree_list (to_tree_list l)) = ncons A t' lreflexivity.A: Type
t, t': ntree A
iht: from_tree (to_tree t') = t'
l: nforest A
ihl: from_tree_list (to_tree_list l) = lncons A t' l = ncons A t' l
Here is the Fibonnaci function.
Notice the as keyword to give a name to subexpression S n. This way Rocq knows that S n is a subterm of the one we started with.
EXERCISE
Define the Fibonnaci function fib using nat_rect directly.
The idea is to recursively produce a pair (fib (n-1), fib n) and then take the second projection snd.
EXERCISE
Define it using course-of-values recursion, a form of strong induction shown below. Of course you need to prove brec first.
Inconsistencies
There are three crucial potential breaking points ofconsistency of type theory:
termination of recursive functions, which needs to be ensured
strict positivity of inductives, which you have seen in the lecture
having type hierarchies rather than a Type : Type rule
Unset Guard Checking.
EXERCISE
Define a function of type nat -> nat and use it to deduce False. Note that this is surprisingly tricky.
n: natn <> S nn: natn <> S n0 <> 1n: nat
ih: n <> S nS n <> S (S n)discriminate.0 <> 1n: nat
ih: n <> S nS n <> S (S n)n: nat
ih: n <> S n
e: S n = S (S n)Falsen: nat
ih: n <> S n
e: S n = S (S n)
H0: n = S nFalseassumption.n: nat
ih: n <> S n
e: S n = S (S n)
H0: n = S nn = S nFalseFalsereflexivity.f 0 = S (f 0)Set Guard Checking. Unset Positivity Checking.
EXERCISE
Define an inductive type bad such that bad <-> ~ bad, use lemma Russel from above to derive a contradiction.
bad <-> ~ badbad <-> ~ badbad -> ~ bad~ bad -> badbad -> ~ badcontradiction.h: bad
nh: bad -> FalseFalse~ bad -> badnh: ~ badbadassumption.nh: ~ badbad -> FalseFalseapply (Russel _ bad_not_bad).FalseSet Positivity Checking.
EXERCISE
We are going to prove a theorem allowing to prove False from Type : Type. Fill in the missing parts below.
X: Typeembeds X XX: Typeembeds X Xreflexivity.X: Typeforall x : X, x = xArguments T { _ _}.A: Tyi
D: A -> Tyiforall s : tree A D, ~ sub s sA: Tyi
D: A -> Tyiforall s : tree A D, ~ sub s sA: Tyi
D: A -> Tyi
a: A
f: D a -> tree A D
ih:= forall d: D a, ~ sub (f d) (f d)~ sub (T a f) (T a f)A: Tyi
D: A -> Tyi
a: A
f: D a -> tree A D
ih:= forall d: D a, ~ sub (f d) (f d)
x: D a
e: f x = T a fFalseA: Tyi
D: A -> Tyi
a: A
f: D a -> tree A D
ih:= forall d: D a, ~ sub (f d) (f d)
x: D a
e: f x = T a fsub (f x) (f x)A: Tyi
D: A -> Tyi
a: A
f: D a -> tree A D
ih:= forall d: D a, ~ sub (f d) (f d)
x: D a
e: f x = T a fsub (f x) (T a f)A: Tyi
D: A -> Tyi
a: A
f: D a -> tree A D
ih:= forall d: D a, ~ sub (f d) (f d)
x: D a
e: f x = T a fexists x0 : D a, f x0 = f xreflexivity.A: Tyi
D: A -> Tyi
a: A
f: D a -> tree A D
ih:= forall d: D a, ~ sub (f d) (f d)
x: D a
e: f x = T a ff x = f xA: Type
D: A -> Tyi
E: Tyi -> A~ embeds (tree A D) (D (E (tree A D)))A: Type
D: A -> Tyi
E: Tyi -> A~ embeds (tree A D) (D (E (tree A D)))A: Type
D: A -> Tyi
E: Tyi -> A
F: tree A D -> D (E (tree A D))
G: D (E (tree A D)) -> tree A D
h:= forall x: tree A D, G (F x) = xFalseA: Type
D: A -> Tyi
E: Tyi -> A
F: tree A D -> D (E (tree A D))
G: D (E (tree A D)) -> tree A D
h:= forall x: tree A D, G (F x) = xsub (T (E (tree A D)) G) (T (E (tree A D)) G)A: Type
D: A -> Tyi
E: Tyi -> A
F: tree A D -> D (E (tree A D))
G: D (E (tree A D)) -> tree A D
h:= forall x: tree A D, G (F x) = xexists x : D (E (tree A D)), G x = T (E (tree A D)) Gapply h.A: Type
D: A -> Tyi
E: Tyi -> A
F: tree A D -> D (E (tree A D))
G: D (E (tree A D)) -> tree A D
h:= forall x: tree A D, G (F x) = xG (F (T (E (tree A D)) G)) = T (E (tree A D)) Gforall X : Tyi, ~ embeds Tyi Xforall X : Tyi, ~ embeds Tyi XX: Tyi
E: Tyi -> X
D: X -> Tyi
h:= forall x: Tyi, D (E x) = xFalseX: Tyi
E: Tyi -> X
D: X -> Tyi
h:= forall x: Tyi, D (E x) = xembeds (tree X D) (D (E (tree X D)))apply embeds_refl.X: Tyi
E: Tyi -> X
D: X -> Tyi
h:= forall x: Tyi, D (E x) = xembeds (tree X D) (tree X D)
We now enable the Type : Type rule.
Unset Universe Checking.FalseFalseapply embeds_refl.embeds Tyi Tyi