Systems and
Formalisms Lab

Coq cheatsheet and gotchas

Coq resources

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:

1 + ?m : nat where ?m : [ |- nat]
fun b : bool => if b then 0 else 1 : bool -> nat

About gives general information about an object:

bool : Set bool is not universe polymorphic Expands to: Inductive Coq.Init.Datatypes.bool
nat : Set nat is not universe polymorphic Expands to: Inductive Coq.Init.Datatypes.nat

Print displays the definition of an object:

Inductive bool : Set := true : bool | false : bool.
Nat.add = fix add (n m : nat) {struct n} : nat := match n with | 0 => m | S p => S (add p m) end : nat -> nat -> nat Arguments Nat.add (n m)%nat_scope

Search finds objects. Its syntax is very flexible:

If you are puzzled by a notation, the Locate command can help:

Notation "x * y" := (Init.Nat.mul x y) : nat_scope (default interpretation) Notation "x * y" := (prod x y) : type_scope

To evaluate an expression, use Compute:

= (6, 8, 2, 2) : nat * nat * nat * nat

Syntax recap

To define a function inline, use fun:

fun x : nat => x + 1 : nat -> nat
fun x : bool => xorb x x : bool -> bool

To perform a case analysis on a value, use match:

fun (b : bool) (x y : nat) => if b then x else y : bool -> nat -> nat -> nat
fun n : nat => match n with | 0 => 1 | S n0 => n0 end : nat -> nat

In Coq, if is just short for match:

fun (b : bool) (x y : nat) => if b then x else y : bool -> nat -> nat -> nat

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.

= 6 : nat

forall x : nat, x = x + 0 + 0

forall x : nat, x = x + 0 + 0
x: nat

x = x + 0 + 0
Nat.add_0_r: forall n : nat, n + 0 = n
plus_n_O: forall n : nat, n = n + 0
x: nat

x = x + 0 + 0
x: nat

x = x + 0
x: nat

x = x
reflexivity. Qed.

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.

= (6, 822) : nat * nat

You can use bullets or braces to structure your proofs:


forall x y z : nat, x + y + z = 0 -> x = 0 /\ y = 0 /\ z = 0

forall x y z : nat, x + y + z = 0 -> x = 0 /\ y = 0 /\ z = 0

forall y z : nat, 0 + y + z = 0 -> 0 = 0 /\ y = 0 /\ z = 0
x: nat
forall y z : nat, S x + y + z = 0 -> S x = 0 /\ y = 0 /\ z = 0

forall y z : nat, 0 + y + z = 0 -> 0 = 0 /\ y = 0 /\ z = 0

forall z : nat, 0 + 0 + z = 0 -> 0 = 0 /\ 0 = 0 /\ z = 0
y: nat
forall z : nat, 0 + S y + z = 0 -> 0 = 0 /\ S y = 0 /\ z = 0

forall z : nat, 0 + 0 + z = 0 -> 0 = 0 /\ 0 = 0 /\ z = 0
z: nat
Heq: z = 0

0 = 0 /\ 0 = 0 /\ z = 0
z: nat
Heq: z = 0

0 = 0
z: nat
Heq: z = 0
0 = 0 /\ 0 = 0
z: nat
Heq: z = 0

0 = 0
reflexivity.
z: nat
Heq: z = 0

0 = 0 /\ 0 = 0
split; reflexivity.
y: nat

forall z : nat, 0 + S y + z = 0 -> 0 = 0 /\ S y = 0 /\ z = 0
y: nat

forall z : nat, 0 + S y + z = 0 -> 0 = 0 /\ S y = 0 /\ z = 0
y, z: nat
Heq: 0 + S y + z = 0

0 = 0 /\ S y = 0 /\ z = 0
y, z: nat
Heq: S (y + z) = 0

0 = 0 /\ S y = 0 /\ z = 0
congruence. }
x: nat

forall y z : nat, S x + y + z = 0 -> S x = 0 /\ y = 0 /\ z = 0
x, y, z: nat
Heq: S x + y + z = 0

S x = 0 /\ y = 0 /\ z = 0
x, y, z: nat
Heq: S (x + y + z) = 0

S x = 0 /\ y = 0 /\ z = 0
congruence. Qed.

A few gotchas

Natural numbers saturate at 0:

= 3 : nat

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 + x

forall x y : nat, x + y = y + x
x: nat

x + 0 = 0 + x
x, y: nat
IHy: x + y = y + x
x + S y = S y + x
x: nat

x + 0 = 0 + x

0 + 0 = 0 + 0
x: nat
IHx: x + 0 = 0 + x
S x + 0 = 0 + S x

0 + 0 = 0 + 0
reflexivity.
x: nat
IHx: x + 0 = 0 + x

S x + 0 = 0 + S x
x: nat
IHx: x + 0 = x

S (x + 0) = S x
x: nat
IHx: x + 0 = x

S x = S x
reflexivity.
x, y: nat
IHy: x + y = y + x

x + S y = S y + x
x, y: nat
IHy: x + y = y + x

x + S y = S (y + x)

Here, IHy is valid only for one specific y.

   Abort.

   

forall x y : nat, x + y = y + x

forall x y : nat, x + y = y + x

forall y : nat, 0 + y = y + 0
x: nat
IHx: forall y : nat, x + y = y + x
forall y : nat, S x + y = y + S x

forall y : nat, 0 + y = y + 0

0 + 0 = 0 + 0
y: nat
IHy: 0 + y = y + 0
0 + S y = S y + 0

0 + 0 = 0 + 0
reflexivity.
y: nat
IHy: 0 + y = y + 0

0 + S y = S y + 0
y: nat
IHy: y = y + 0

S y = S (y + 0)
y: nat
IHy: y = y + 0

S y = S y
reflexivity.
x: nat
IHx: forall y : nat, x + y = y + x

forall y : nat, S x + y = y + S x
x: nat
IHx: forall y : nat, x + y = y + x
y: nat

S (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 1

forall n : nat, factorial n = factorial_acc n 1

factorial 0 = factorial_acc 0 1
n: nat
IHn: factorial n = factorial_acc n 1
factorial (S n) = factorial_acc (S n) 1

factorial 0 = factorial_acc 0 1

1 = 1
reflexivity.
n: nat
IHn: factorial n = factorial_acc n 1

factorial (S n) = factorial_acc (S n) 1
n: nat
IHn: factorial n = factorial_acc n 1

factorial n + n * factorial n = factorial_acc n (S (n * 1))
Nat.mul_1_r: forall n : nat, n * 1 = n
n: nat
IHn: factorial n = factorial_acc n 1

factorial n + n * factorial n = factorial_acc n (S (n * 1))
n: nat
IHn: factorial n = factorial_acc n 1

factorial 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 acc

forall acc n : nat, factorial n * acc = factorial_acc n acc
acc: nat

factorial 0 * acc = factorial_acc 0 acc
acc, n: nat
IHn: factorial n * acc = factorial_acc n acc
factorial (S n) * acc = factorial_acc (S n) acc
acc: nat

factorial 0 * acc = factorial_acc 0 acc
acc: nat

acc + 0 = acc
acc: nat

acc = acc
reflexivity.
acc, n: nat
IHn: factorial n * acc = factorial_acc n acc

factorial (S n) * acc = factorial_acc (S 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)
The command has indeed failed with message: Found no subterm matching "factorial_acc n acc" in the current goal.
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 acc

forall n acc : nat, factorial n * acc = factorial_acc n acc

forall acc : nat, factorial 0 * acc = factorial_acc 0 acc
n: nat
IHn: forall acc : nat, factorial n * acc = factorial_acc n acc
forall acc : nat, factorial (S n) * acc = factorial_acc (S n) acc

forall acc : nat, factorial 0 * acc = factorial_acc 0 acc
acc: nat

factorial 0 * acc = factorial_acc 0 acc
acc: nat

acc + 0 = acc
acc: nat

acc = acc
reflexivity.
n: nat
IHn: forall acc : nat, factorial n * acc = factorial_acc n acc

forall acc : nat, factorial (S n) * acc = factorial_acc (S n) acc
n: nat
IHn: forall acc : nat, factorial n * acc = factorial_acc n acc
acc: nat

factorial (S n) * acc = factorial_acc (S n) acc
n: 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!

    
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)
lia. Qed.