Coq cheatsheet and gotchas
Coq resources
For help on standard Coq tactics, consult Coq's reference manual, starting from the indices. The manual can be overwhelming, so it's best used for looking up fine details.
Useful commands
Coq comes with many predefined types, functions, and theorems (“objects”). The most important commands to help you discover them are Check
, About
, Print
, Search
, and Compute
. Try the following examples:
Check
gives the type of any term, even with holes:
About
gives general information about an object:
Print
displays the definition of an object:
Search
finds objects. Its syntax is very flexible:
Find functions of type
nat -> nat -> bool
.Find theorems about
||
in moduleNat
.Find theorems about subtraction and equality whose name contains
_distr
.Search for a lemma proving the symmetry of
=
.
If you are puzzled by a notation, the Locate
command can help:
To evaluate an expression, use Compute
:
Syntax recap
To define a function inline, use fun
:
To perform a case analysis on a value, use match
:
In Coq, if
is just short for match
:
To define a global object, use Definition
or Lemma
(Theorem
is an alias of Lemma
):
Definition choose (b: bool) (x y: nat) := if b then x else y.forall x : nat, x = x + 0 + 0forall x : nat, x = x + 0 + 0x: natx = x + 0 + 0x: natx = x + 0 + 0x: natx = x + 0reflexivity. Qed.x: natx = x
Recursive functions use the keyword Fixpoint
:
Fixpoint do_n_times (ntimes: nat) (step: nat -> nat) (start_from: nat) := match ntimes with | 0 => start_from | S ntimes' => step (do_n_times ntimes' step start_from) end.
You can use bullets or braces to structure your proofs:
forall x y z : nat, x + y + z = 0 -> x = 0 /\ y = 0 /\ z = 0forall x y z : nat, x + y + z = 0 -> x = 0 /\ y = 0 /\ z = 0forall y z : nat, 0 + y + z = 0 -> 0 = 0 /\ y = 0 /\ z = 0x: natforall y z : nat, S x + y + z = 0 -> S x = 0 /\ y = 0 /\ z = 0forall y z : nat, 0 + y + z = 0 -> 0 = 0 /\ y = 0 /\ z = 0forall z : nat, 0 + 0 + z = 0 -> 0 = 0 /\ 0 = 0 /\ z = 0y: natforall z : nat, 0 + S y + z = 0 -> 0 = 0 /\ S y = 0 /\ z = 0forall z : nat, 0 + 0 + z = 0 -> 0 = 0 /\ 0 = 0 /\ z = 0z: nat
Heq: z = 00 = 0 /\ 0 = 0 /\ z = 0z: nat
Heq: z = 00 = 0z: nat
Heq: z = 00 = 0 /\ 0 = 0reflexivity.z: nat
Heq: z = 00 = 0split; reflexivity.z: nat
Heq: z = 00 = 0 /\ 0 = 0y: natforall z : nat, 0 + S y + z = 0 -> 0 = 0 /\ S y = 0 /\ z = 0y: natforall z : nat, 0 + S y + z = 0 -> 0 = 0 /\ S y = 0 /\ z = 0y, z: nat
Heq: 0 + S y + z = 00 = 0 /\ S y = 0 /\ z = 0congruence. }y, z: nat
Heq: S (y + z) = 00 = 0 /\ S y = 0 /\ z = 0x: natforall y z : nat, S x + y + z = 0 -> S x = 0 /\ y = 0 /\ z = 0x, y, z: nat
Heq: S x + y + z = 0S x = 0 /\ y = 0 /\ z = 0congruence. Qed.x, y, z: nat
Heq: S (x + y + z) = 0S x = 0 /\ y = 0 /\ z = 0
A few gotchas
Natural numbers saturate at 0:
The order in which you perform induction on variables matters: if x
comes before y
and you do induction on y
, your induction hypothesis will not be general enough.
forall x y : nat, x + y = y + xforall x y : nat, x + y = y + xx: natx + 0 = 0 + xx, y: nat
IHy: x + y = y + xx + S y = S y + xx: natx + 0 = 0 + x0 + 0 = 0 + 0x: nat
IHx: x + 0 = 0 + xS x + 0 = 0 + S xreflexivity.0 + 0 = 0 + 0x: nat
IHx: x + 0 = 0 + xS x + 0 = 0 + S xx: nat
IHx: x + 0 = xS (x + 0) = S xreflexivity.x: nat
IHx: x + 0 = xS x = S xx, y: nat
IHy: x + y = y + xx + S y = S y + xx, y: nat
IHy: x + y = y + xx + S y = S (y + x)
Here, IHy
is valid only for one specific y
.
Abort.forall x y : nat, x + y = y + xforall x y : nat, x + y = y + xforall y : nat, 0 + y = y + 0x: nat
IHx: forall y : nat, x + y = y + xforall y : nat, S x + y = y + S xforall y : nat, 0 + y = y + 00 + 0 = 0 + 0y: nat
IHy: 0 + y = y + 00 + S y = S y + 0reflexivity.0 + 0 = 0 + 0y: nat
IHy: 0 + y = y + 00 + S y = S y + 0y: nat
IHy: y = y + 0S y = S (y + 0)reflexivity.y: nat
IHy: y = y + 0S y = S yx: nat
IHx: forall y : nat, x + y = y + xforall y : nat, S x + y = y + S xx: nat
IHx: forall y : nat, x + y = y + x
y: natS (x + y) = y + S x
Here, IHx
starts with forall y
.
Abort.
Here's an example where this subtlety matters:
Fixpoint factorial (n: nat) := match n with | O => 1 | S n' => n * factorial n' end. Fixpoint factorial_acc (n: nat) (acc: nat) := match n with | O => acc | S n' => factorial_acc n' (n * acc) end.
First attempt, but our lemma is too weak:
forall n : nat, factorial n = factorial_acc n 1forall n : nat, factorial n = factorial_acc n 1factorial 0 = factorial_acc 0 1n: nat
IHn: factorial n = factorial_acc n 1factorial (S n) = factorial_acc (S n) 1factorial 0 = factorial_acc 0 1reflexivity.1 = 1n: nat
IHn: factorial n = factorial_acc n 1factorial (S n) = factorial_acc (S n) 1n: nat
IHn: factorial n = factorial_acc n 1factorial n + n * factorial n = factorial_acc n (S (n * 1))n: nat
IHn: factorial n = factorial_acc n 1factorial n + n * factorial n = factorial_acc n (S (n * 1))n: nat
IHn: factorial n = factorial_acc n 1factorial n + n * factorial n = factorial_acc n (S n)
Stuck! We have no way to simplify factorial_acc n (S n)
.
Abort.
Second attempt: a generalized lemma, but we put the acc
first, so induction will not generalize it.
forall acc n : nat, factorial n * acc = factorial_acc n accforall acc n : nat, factorial n * acc = factorial_acc n accacc: natfactorial 0 * acc = factorial_acc 0 accacc, n: nat
IHn: factorial n * acc = factorial_acc n accfactorial (S n) * acc = factorial_acc (S n) accacc: natfactorial 0 * acc = factorial_acc 0 accacc: natacc + 0 = accreflexivity.acc: natacc = accacc, n: nat
IHn: factorial n * acc = factorial_acc n accfactorial (S n) * acc = factorial_acc (S n) accacc, n: nat
IHn: factorial n * acc = factorial_acc n acc(factorial n + n * factorial n) * acc = factorial_acc n (acc + n * acc)acc, n: nat
IHn: factorial n * acc = factorial_acc n acc(factorial n + n * factorial n) * acc = factorial_acc n (acc + n * acc)
Stuck! IHn is too weak.
Abort.
Third time's the charm! Note how we ordered n
and acc
.
forall n acc : nat, factorial n * acc = factorial_acc n accforall n acc : nat, factorial n * acc = factorial_acc n accforall acc : nat, factorial 0 * acc = factorial_acc 0 accn: nat
IHn: forall acc : nat, factorial n * acc = factorial_acc n accforall acc : nat, factorial (S n) * acc = factorial_acc (S n) accforall acc : nat, factorial 0 * acc = factorial_acc 0 accacc: natfactorial 0 * acc = factorial_acc 0 accacc: natacc + 0 = accreflexivity.acc: natacc = accn: nat
IHn: forall acc : nat, factorial n * acc = factorial_acc n accforall acc : nat, factorial (S n) * acc = factorial_acc (S n) accn: nat
IHn: forall acc : nat, factorial n * acc = factorial_acc n acc
acc: natfactorial (S n) * acc = factorial_acc (S n) accn: nat
IHn: forall acc : nat, factorial n * acc = factorial_acc n acc
acc: nat(factorial n + n * factorial n) * acc = factorial_acc n (acc + n * acc)
IHn is strong enough now!
lia. Qed.n: nat
IHn: forall acc : nat, factorial n * acc = factorial_acc n acc
acc: nat(factorial n + n * factorial n) * acc = factorial n * (acc + n * acc)