UP | HOME

Coq

\( \renewcommand{\vec}[1]{\mathbf{#1}} \newcommand{\mat}[1]{\mathbf{#1}} \DeclareMathOperator{\argmax}{arg\,max} \DeclareMathOperator{\argmin}{arg\,min} \DeclareMathOperator{\pto}{\rightharpoonup} \DeclareMathOperator{\LIM}{LIM} % algebra \DeclareMathOperator{\Hom}{Hom} \DeclareMathOperator{\Span}{Span} \DeclareMathOperator{\Ran}{Ran} \DeclareMathOperator{\Ker}{Ker} \DeclareMathOperator{\Nul}{Nul} \DeclareMathOperator{\Col}{Col} \DeclareMathOperator{\Row}{Row} \DeclareMathOperator{\Tr}{Tr} \DeclareMathOperator{\rank}{rank} \DeclareMathOperator{\proj}{proj} \DeclareMathOperator{\diag}{diag} % probability and statistics \DeclareMathOperator{\E}{\mathbb{E}} \DeclareMathOperator{\prob}{\mathbb{P}} \DeclareMathOperator{\indep}{\perp \!\!\! \perp} \DeclareMathOperator{\Var}{\mathbb{V}} \DeclareMathOperator{\Cov}{Cov} \DeclareMathOperator{\Cor}{Cor} % programs \DeclareMathOperator{\Val}{Val} \DeclareMathOperator{\Vars}{Vars} \DeclareMathOperator{\AExp}{AExp} \DeclareMathOperator{\BExp}{BExp} \DeclareMathOperator{\AND}{\mathsf{and}} \DeclareMathOperator{\OR}{\mathsf{or}} \DeclareMathOperator{\NOT}{\mathsf{not}} \DeclareMathOperator{\TRUE}{\mathsf{true}} \DeclareMathOperator{\FALSE}{\mathsf{false}} \DeclareMathOperator{\SKIP}{\mathsf{skip}} \DeclareMathOperator{\WHILE}{\mathsf{while}} \DeclareMathOperator{\DO}{\mathsf{do}} \DeclareMathOperator{\OD}{\mathsf{od}} \DeclareMathOperator{\IF}{\mathsf{if}} \DeclareMathOperator{\FI}{\mathsf{fi}} \DeclareMathOperator{\THEN}{\mathsf{then}} \DeclareMathOperator{\ELSE}{\mathsf{else}} \DeclareMathOperator{\FOR}{\mathsf{for}} \DeclareMathOperator{\TERM}{\mathsf{Term}} \DeclareMathOperator{\HALT}{\mathsf{Halt}} \DeclareMathOperator{\BREAK}{\mathsf{Break}} \DeclareMathOperator{\CONTINUE}{\mathsf{Continue}} \DeclareMathOperator{\COND}{\mathsf{cond}} \DeclareMathOperator{\UNDEFINED}{\mathsf{undefined}} \)

1. Installation

1.1. Coq

1.1.1. -Installing Coq Using DMG-

https://github.com/coq/platform/blob/2022.09.1/doc/README_macOS.md

To use installed tools from the command line (e.g., in the case where coq is run by editor-support packages): Add the folder /Applications/Coq-Platform~<version>.app/Contents/Resources/bin to your $PATH, e.g. by running

sudo sh -c "echo '/Applications/Coq-Platform~<version>.app/Contents/Resources/bin' > /etc/paths.d/coq"

This method has some disadvantages. See the link above.

1.1.2. Recommended

brew install coq

1.2. User Interfaces

https://coq.inria.fr/user-interfaces.html

Editor-support packages:

Or

Standalone interfaces: the CoqIDE alongside Coq.

2. Basic Usage

Every statement must end with ..

Section:

  • Begins with Section <name>., ends with End <name>..
  • One file can have multiple sections.
  • Variables are independent over different sections.

2.1. Declare variable

Variable p: <type>
Declare a variable p of type <type>.
  • multiple variables Variables p q r: <type>.

2.2. Propositions

Propositions are of type Prop.

Operations are:

  • Implication -> : Right associativity. p -> q -> p means p -> (q -> p)
  • And /\
  • Or \/
  • Equal =
  • Not ~
  • Iff <->

Remark: a /\ b -> c is equivalent to a -> b -> c. Usually, the latter is preferred.

Can involve declared variables and universal quantified or existential quantified variables.

Quantifications:

  • Universal quantification forall x : <type>, <predicate> .
  • Existential quantification exists x : <type>, <predicate> .

2.3. Declare theorem

Theorem <name>:, Lemma <name>:, etc.
followed by a proposition. Theorems will appear in goals.
Conjecture <name>:
no proof needed. does not appear in goals.

2.4. Proof

Proof
Starts with Proof., ends with Qed. (only when the proof is completed).

3. Tactics

A tactic is one step of reasoning.

  • split: split the iff to two implications.
  • assert Q: adds Q to the current subgoal. prove Q and use it as a lemma. Q can be of the form (P -> Q).
  • auto: check simple implications as many times as possible.
  • tauto: check tautology.
  • specialize H with (p := ...): specialize the uniformly quantified variable p in the hypothesis H to some expression.
  • left: choose to prove the left side of a disjunction.

pose proof em. where em is an axiom without proof.

exists x. x satsifies the existential quantification.

elim H:

3.1. intro, intros

intro
Introduces an assumption / variable in the goals into the hypothesis.
  • intro x : specify the variable name as x (otherwise up to Coq)
  • intro x eq : … and specify the name of hypothesis on x as eq (otherwise H etc.)

E.g.,

Theorem plus0:
  forall n : nat , 0 + n = n.
Proof.
  intro x. (* becomes 0 + x = x *)
  compute.
  reflexivity.
Qed.
intros
Does intro as many times as possible.
  • intros n m eqn eqm : for multiple variables, similar.
  • intros n : If there are more than two variables, this introduces only one variable as n.

3.2. assumption

assumption

looks for a hypothesis which matches with the goal and prove the goal.

  • ; assumption: apply assumption on existing branches. See [[*Exercise 2-2

(destruct)][The alternative of ex2-2]].

3.3. apply

apply H
  • If the current goal is q, and there is a hypothesis H: p -> q, then this command does backward reasoning the current goal becomes the premise p.
  • It also separate more subgoals if H is a multi-hop implication, e.g., if H: p -> r -> q then the subgoals become p and q.
  • (like rewrite) If the current goal and H are both equations, the command substitute (one side of) the goal equation using H (based on pattern matching).
  • apply thm with (m := c) : apply a theorem thm with variable m in the theorem matched with variable c (in the current context). E.g., (prerequisite: List)

    Theorem trans_eq : forall (X: Type) (n m o : X),
      n = m -> m = o -> n = o.
    Proof.
      intros X n m o eq1 eq2.
      rewrite -> eq1.
      assumption.
    Qed.
    
    Example trans_eq_example' : forall (a b c d e f : nat),
         [a;b] = [c;d] ->
         [c;d] = [e;f] ->
         [a;b] = [e;f].
    Proof.
      intros a b c d e f eq1 eq2.
      (* cannot directly apply trans_eq. *)
      apply trans_eq with (m := [c; d]).
      apply eq1.
      apply eq2.
    Qed.
    
  • apply H in H0: See in H.

3.3.1. Exercise 2-1 (apply)

Section test.
Variables P Q R S T: Prop.
Theorem ex1 : (P->Q)->(Q->R)->P->R.
Proof.
intros.
apply H0. (* goal: R, H0: Q -> R *)
apply H.  (* goal: Q, H: P -> Q*)
assumption.
Qed.

3.4. simpl

simpl
Simply the goal based on definitions of functions / variables.

3.5. destruct

destruct H
For case analysis.
  • If H is a conjunction, i.e., H: p /\ q, then this command decompose it into two hypothesis H0: p and H1: q and create two branches correspondingly. That means that when the current subgoal is completed, and it moves to the branch for the next goal, the hypothesis will turn back into the conjunction form H: p /\ q.n
  • destruct x as [x1 | x2 | ...] : Destruct a variable x into multiple cases based on the constructors of the type of x, with each case named as the corresponding variable in [] separated by |. If a constructor of the type of x consists of multiple arguments, list those arguments like [... | a1 a2 | ...]. See 5.1.1
  • destruct x as [...] eqn:E : Additionally put the equation of x for the current case into hypothesis E.

E.g.,

Variables P Q R : Prop.
Hypothesis H : P -> Q -> R.
Lemma l : P /\ Q -> R.
Proof.
  intro.
  apply H. (* Two goals: P, Q. The 1st goal: P *)
  destruct H0.
  assumption. (* The next goal: Q, same steps *)
  destruct H0.
  assumption.
Qed.

(* Alternatively, destruct H0; assumption *)
Lemma l1 : P /\ Q -> R.
Proof.
  intros.
  destruct H0.
  apply H ; assumption.
Qed.

E.g., (though E is not used)

Theorem th4:
  forall b: bool, negb (negb b) = b.
Proof.
  intros.
  destruct b eqn:E.
  reflexivity. ()
  reflexivity.
Qed.

E.g.,

Theorem th3:
  forall n: nat, (n+1 =? 0) = false.
Proof.
  intros.
  destruct n as [ | n'] eqn:E.
  compute. (* E : n = 0, goal : (0 + 1 =? 0) = false *)
  reflexivity.
  compute. (* E : n = S n', goal : (S n' + 1 =? 0) = false *)
  reflexivity.
Qed.

E.g.,

Theorem th5:
  forall b c: bool, andb b c = andb c b. (* auto detect types for b, c *)
Proof.
  intros.
  destruct b eqn:Eb.   (* destruct b; desctruct c; reflexivity, on every subtree do destruct c, reflexivity *)
  destruct c eqn:Ec.
  reflexivity.
  reflexivity.
  destruct c.
  reflexivity.
  reflexivity.
Qed.

3.6. Bullet point, -, +

Bullet point -, +
Use bullet point to focus the current goal.

3.7. pose

Apply already proven theorems or axioms.

3.8. symmetry

symmetry
If the goal is an equation, swaps the two sides.

3.9. injection

injection H
If H: cons a = cons b is an equation of the same constructor, then adds a hypothesis about the injection of the constructor, a = b -> (similar for multivariate cases), before the goal (sufficient condition).
  • injection H as H0 : instead of adding the hypothesis to the goal to form an implication, adds the hypothesis separately as H0.

E.g.,

Theorem S_injective' :
  forall n m : nat,
    S n = S m -> n = m.
Proof.
  intros n m H. (* H: S n = S m *)
  injection H as H0. (* H0: n = m *)
  apply H0.
Qed.

3.10. discriminate

discriminate H
If H is a contradiction, then it implies the goal anyway.
  • discriminate : tries every hypothesis

Note that different constructors cannot be equal, which can form contradictions.

3.11. f_equal

f_equal
If there is a hypothesis H: n = m (similar for multivariate cases), and the goal is f n = f m, where f is a function (including constructor), then this proves the goal.

3.12. in H

<tactic> in H
Apply tactics to hypothesis.
  • simpl in H : simply H
  • symmetry in H :
  • apply H in H0 as H1 : Forward reason H0 using H and let the result be H1. E.g.,

    Theorem trivial6:
      forall (n m p q : nat),
      (n = m -> p = q) ->
        m = n -> q = p.
    Proof.
      intros.
      symmetry in H0. (* H: n = m -> p = q, H0 : n = m *)
      apply H in H0 as H1. (* H1: p = q *)
      symmetry in H1.
      assumption.
    Qed.
    

3.13. generalize dependent

generalize dependent n
Take variable n and combine the hypothesis and the goal into a universal quantification predicate of n, like forall n : nat, <hypothesis> -> goal.

3.14. Ltac

Ltac <name> H :=~
Define a tactics named <name> with argument H as after :=. Use the tactic later by calling <name>.

3.15. lia

lia
Solves Presburger arithmetic (no multiplication).

3.16. repeat

repeat <tactic 1>
Repeatedly apply <tactic 1>.
repeat <tactic 1>; <tactic 2>
apply <tactic 2> after applying <tactic 1> exhaustively.

3.17. inversion

inversion H
Find necessary conditions for H and put them in hypotheses.

3.18. unfold

unfold
replace a definition with its actual expression.

3.19. remember

remember <expression> as n
Use n to represent <expression>. The equation n = <expression> will be added to hypotheses and instances of <expression> will be substituted by n.

4. Types, Functions and Related Proofs

4.1. Check

Check <expr>. to check the type of the expression.

4.2. Inductively Defining Types Inductive

Inductive <name> : Type :=
Defines a type called <name> and its constructors.

E.g.,

Inductive rgb : Type :=
| red
| green
| blue.

, where rgb is a type and red, green, and blue are its constructors.

Check the typ,

Check red. (* white : color *)
Constructors with parameters
a constructor can accept parameters of other types or the same type.

E.g.,

Inductive color : Type :=
| white
| black
| primary (p: rgb).

Check primary. (* primary : rgb -> color *)
Check primary red. (* primary : color *)

4.2.1. Natural Number Type Example

E.g., to define the natural number type (since there is a built-in nat type, we use Module to isolate the definitions):

Module mynat.

  Inductive nat : Type :=
  | O (* 0 *)
  | S(n: nat). (* successor *)

  Check O.
  Check S O. (* 1 *)
  Check S (S O). (* 2 *)

End mynat.

, where there are only two constructors: A number is either 0 (represented as O), or a successor of some number (represented as S n). Note that these symbols do not carry any meanings on their own, e.g., S could be any map : nat -> nat, but we implement arithmatic rules to assign the meanings.

4.3. Built-in Types

4.3.1. Importing Libraries

Require Import Nat. (* Capitalize the initial character *)
Require Import Bool.

4.3.2. nat examples

Require Import Nat.

Check O.
Check S O.
Check S (S 1).

Definition minustwo (a : nat) : nat :=
  match a with
  | O => O
  | S O  => O
  | S (S n) => n
  end.

Compute minustwo 6.

4.4. Defining Functions Definition, match with

4.4.1. Basic pattern matching

Define a function based on pattern matching:

  • Much match all cases of the type.
  • Matching is carried out from top to down.
  • _ matches the remaining cases.
Inductive day : Type :=
| monday
| tuesday
| wednesday
| thursday
| friday
| saturday
| sunday.

Definition next_workday (_d : day) : day :=
  match _d with
  | monday => tuesday
  | tuesday => wednesday
  | wednesday => thursday
  | thursday => friday
  | friday => monday (* or _ => monday *)
  | saturday => monday
  | sunday => monday
  end.

Nested matching: E.g.,

Definition andb2 (b1 b2: bool) : bool :=
  match b1 with
  | false => false
  | true => match b2 with
           | true => true
           | false => false
           end
  end.

4.4.2. Applying the function, Compute.

E.g.,

Compute next_workday wednesday. (* = thursday : day *)
Compute next_workday (next_workday tuesday). (* the same *)

4.4.3. Matching constructor with parameter

E.g., natural number:

Module mynat.

  Inductive nat : Type :=
  | O
  | S(n: nat).

  Definition pred (a: nat) : nat :=
    match a with
    | O => O (* nonsense *)
    | S n => n
    end.

  Compute pred (S (S (S O))). (* S (S O) *)

End mynat.

a is either O or S of something. If a is S n, then a is the successor of n and n is the predecessor of a.

E.g., with _:

Definition monochrome (c : color) : bool :=
  match c with
  | white => true
  | black => true
  | primary(_) => false
  end.

4.4.4. Multivariate functions

E.g.,

Definition andb3 (b1 b2: bool): bool :=
  match (b1, b2) with
  | (true, true) => true
  | _ => false (* (_, _) => false *)
  end.

Compute andb3 true true.

4.4.5. Invoking other functions

E.g., assuming negb, the negation of a Boolean value is defined,

Definition nandb (b1 b2: bool) : bool :=
  negb (andb3 b1 b2).
Check nandb.

Check can also be used to check the type of a function.

4.4.6. Notation

Define a notation for a function.

E.g., assuming anb3 : bool -> bool -> bool is defined,

Notation "x && y" := (andb3 x y).
Print "x && y". (* Arguments andb3 b1 b2 *)
Compute true && false. (* = false : bool *)

4.4.7. Recursive functions, Fixpoint

E.g.,

Require Import Nat.
Require Import Bool.

Fixpoint myeven (a: nat): bool :=
  match a with
  | O => true
  | S O => false
  | S (S n) => myeven n
  end.
  1. More examples with multivariate functions
    Fixpoint plus (n m: nat) : nat :=
      match n with
      | O => m
      | S n' => S (plus n' m)
      end.
    
    Fixpoint mult(n m: nat) :nat :=
      match n with
      | O => O
      | S n' => plus m (mult n' m)
      end.
    
    Fixpoint minus(n m: nat) : nat :=
      match n, m with
      | O, _ => O (* <= 0 *)
      | _, O => n
      | S n', S m' => minus n' m'
      end.
    
    Definition geq(n m: nat): bool :=
      match minus m n with
      | O => true
      | _ => false
      end.
    
    Definition eq1(n m: nat) : bool :=
      andb (geq n m) (geq m n).
    
    Fixpoint eq2(n m: nat): bool :=
      match n, m with
      | O, O => true
      | O, _ => false
      | _, O => false
      | S n', S m' => eq2 n' m'
      end.
    

4.5. Proofs with Functions and Types

4.5.1. compute.

Compute the application of functions in the goal.

E.g.,

Example test: (* same as Lemma etc., usually for test *)
  next_workday (next_workday tuesday) = thursday.
Proof.
  compute. (* becomes thursday = thursday *)
  reflexivity.
Qed.

Note that compute follows the matching process defined by the function, so we have to make sure the arguments can be matched with some case. In particular, Coq does not know communtativity

E.g., plus is defined as (Print "x + y".):

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 add (n m)%nat_scope

So we cannot simply prove n + 0 = n by compute.

4.5.2. reflexivity

Check the reflexive property in the goal. It can do more than checking trivial equality in that it also applies compute, etc. to simplify the goal.

4.5.3. =, =?

= produces a propositon.

E.g.,

Check 2=3. (* : Prop *)

=? produces a boolean value.

E.g.,

Check 2=?3. (* : bool *)
Compute 2=?3. (* = false : bool *)

4.5.4. rewrite

rewrite <- H: Rewrite the goal based on hypothesis H. <- or -> specify which side of the equation to rewrite.

E.g.,

Theorem th1:
  forall n m: nat, n = m -> n + n = m + m.
Proof.
  intros. (* H : n = m, goal : n + n = m + m *)
  rewrite <- H. (* becomes n + n = n + n. If -> H, becomes m + m = m + m*)
  reflexivity.
Qed.

4.5.5. Built-in Theorem

mult_n_O : forall n : nat, 0 = n * 0

mult_n_Sm : forall n m : nat, n * m + n = n * S m

4.5.6. induction n.

Do an induction on n based on the definitin of the type of n. The induction hypothesis will be added to hypothesis list.

Theorem th6:
  forall n : nat, n + 0 = n.
Proof.
  intros.
  induction n as [ | n'].
  compute.
  reflexivity.
  simpl. (* IHn' : n' + 0 = n', goal : S (n' + 0) = S n' *)
  rewrite -> IHn'.
  reflexivity.
Qed.

The simpl is based on the definition of the arithmetic and the type.

5. Lecture 17&18: Lists, Options, and Polymorphism

We are going to define our own module of list.

Require Import Nat.
Module LST.

5.1. Pair

Inductive natprod : Type :=
| pair (n1 n2 : nat).

Definition fst (p: natprod) : nat :=
  match p with
  | pair x _ => x
  end.

Definition snd (p: natprod) : nat :=
  match p with
  | pair _ y => y
  end.

Definition swap_pair (p: natprod) : natprod :=
  match p with
  | (x, y) => (y, x)
  end.

Notation "( x , y )" := (pair x y).

5.1.1. Example Proof with destruct

Theorem trivial :
  forall n m : nat,
    (n, m) = (fst(n, m), snd(n, m)).
Proof.
  simpl.
  reflexivity.
Qed.
Theorem trivial2 :
  forall p : natprod,
    p = (fst p, snd p).
Proof.
  intros p.
  destruct p as [n m]. (* (n, m) = (fst (n, m), snd (n, m)) *)
  apply trivial.
Qed.

5.2. List

Definition of list
We construct a list inductively as a beginning item and the tail.
Inductive natlist : Type :=
| nil
| cons (n : nat) (l : natlist).
(* cons stands for "constructor". It's just a name *)
(* n represent the beginning item, l represent the tail of the list *)

Notation "x :: l" := (cons x l) (at level 60, right associativity).
Notation "[]" := nil.
Notation "[ x ; .. ; y ]" := (cons x .. (cons y nil) ..).

right associativity: E.g., 1 :: 2 :: 3 = 1 :: (2 :: 3). Left associativity violates the type of the constructor.

repeat, length, append
  Fixpoint repeat(n count: nat) : natlist :=
    match count with
    | 0 => []
    | S count' => n :: (repeat n count')
    end.

  Fixpoint length(l: natlist) : nat :=
    match l with
    | [] => 0
    | _ :: l' => S (length l')
    end.

Fixpoint append(l1 l2: natlist) : natlist :=
  match l1 with
  | [] => l2
  | h :: t =>  h :: (append t l2)
  end.

Notation "x ++ y" := (append x y)
                     (right associativity, at level 60).
head, taill, last_element
Some of the following functions have default return value for special cases:
Definition hd (default: nat)(l: natlist) : nat :=
  match l with
  | [] => default
  | h :: _ => h
  end.

Definition tl (l: natlist) : natlist :=
  match l with
  | [] => []
  | _ :: t => t
  end.

Fixpoint last_element (default: nat)(l: natlist) : nat :=
  match l with
  | [] => default
  | n :: [] => n
  | n :: t => last_element default t
  end.

E.g.,

  Theorem tl_length_pred :
      forall lst: natlist,
        pred (length lst) = length (tl lst).
  Proof.
    intros.
    destruct lst as [ | h t].
    compute.
    reflexivity.
    simpl. (* pred (length (h :: t)) = length (tl (h :: t)) *)
    reflexivity.
  Qed.

Theorem app_assoc :
  forall lst1 lst2 lst3 : natlist,
    (lst1 ++ lst2) ++ lst3 = lst1 ++ (lst2 ++ lst3).
Proof.
  intros.
  induction lst1 as [ | h1 t1].
  simpl.
  reflexivity.
  simpl.
  rewrite IHt1.
  reflexivity.
Qed.

Theorem app_length :
  forall lst1 lst2 : natlist,
  length (lst1 ++ lst2) = (length lst1) + (length lst2).
Proof.
  intros.
  induction lst1 as [ | h1 t1].
  simpl.
  reflexivity.
  simpl.
  rewrite <- IHt1.
  reflexivity.
Qed.
reverse
Fixpoint rev (lst: natlist) : natlist :=
  match lst with
  | [] => []
  | h :: t => rev t ++ [h]
  end.

nat_add_com is a lemma: forall a b: nat, a + b = b + a.

Theorem rev_length:
  forall lst: natlist,
  length (rev lst) = length lst.
Proof.
  intros.
  induction lst as [| h t].
  compute.
  reflexivity.
  simpl.
  rewrite app_length.
  simpl.
  rewrite IHt. (* length t + 1 = S (length t) *)
  rewrite nat_add_com. (* 1 + length t = S (length t) *)
  simpl.
  reflexivity.
Qed.
nth element
Fixpoint nth_bad (lst: natlist)(n : nat) : nat :=
  match lst with
  | [] => 420 (* any default value *)
  | h :: t =>
    match n with
    | 0 => h
    | S k => nth_bad t k
    end
  end.

Cannot distinguish the error value and the nth element.

Return None for empty list and Some if the nth element exists.

Inductive natoption : Type :=
  | None
  | Some (n: nat).

Fixpoint nth_error (lst: natlist)(n:nat) : natoption :=
  match lst with
  | [] => None
  | h :: t =>
      match n with
      | 0 => Some h
      | S k => nth_error t k
      end
   end.

5.3. Partial Map

  • Definition of paritial map:
Module PartialMap.

Inductive partial_map : Type :=
  | Empty
  | Binding (k: nat)(v: nat)(m: partial_map).
update, find
Definition update(k : nat)(v: nat)(m: partial_map) : partial_map :=
  Binding k v m.

Fixpoint find(k: nat) (m: partial_map) : natoption :=
  match m with
  | Empty => None
  | Binding k2 v m' =>
      if k =? k2 then Some v else find k m'
  end.
Theorem find_update:
  forall (m: partial_map) (k v: nat),
    find k (update k v m) = Some v.
Proof.
  intros.
  simpl.
  rewrite eq_refl.
  reflexivity.
Qed.

5.4. Search

  • Search: search theorems by name or pattern.
Search rev. (* theorems whose name contains 'rev' *)
Search (_ + _ = _ + _).

5.5. Polymorphism

Polymorphic list
list has an argument specifying the type of this list.
Module Polymorphism.

(* Polymorphic Lists *)

Inductive list (X: Type) : Type :=
  | nil
  | cons (x: X)(l: list X).

list X is a Type. Use the constructor with the type argument, e.g., nil nat is an empty list of type nat and cons nat 3 (nil nat) is a list of type nat with only one element 3.

Functions about polymorphic lists also have a type argument.

Fixpoint repeat (X: Type)(x: X)(count: nat) : list X :=
  match count with
  | 0 => nil X
  | S count' => cons X x (repeat X x count')
  end.
Automatically detecting typp argument

E.g., the above function can be simplified as

Fixpoint repeat'' X x count : list X :=
  match count with
  | 0 => nil _
  | S count' => cons _ x (repeat'' _ x count')
  end.

E.g.,

Check cons _ 1 (cons _ 2 (cons _ 3 (nil _))). (* detect that _ should be nat *)
Implicit arguments, Arguments {}
Arguments nil {X}.
Arguments cons {X}.
Arguments repeat {X}.

This claims that coq has to find out the implicit argument if omitted.

E.g.,

Check cons 1 (cons 2 (cons 3 nil)). (* : list nat *)

Use of {} syntax in function definitions:

Fixpoint repeat''' {X: Type} (x: X) (count: nat) : list X :=
  match count with
  | 0 => nil
  | S count' => cons x (repeat''' x count')
  end.

Fixpoint app {X: Type}(l1 l2: list X) : list X :=
  match l1 with
  | nil => l2
  | cons h t => cons h (app t l2)
  end.

Fixpoint rev {X: Type}(l: list X) : list X :=
  match l with
  | nil => nil
  | cons h t => app (rev t)(cons h nil)
  end.

Fixpoint length {X: Type}(l : list X) : nat :=
  match l with
  | nil => 0
  | cons _ l' => S (length l')
  end.

E.g.,

Definition x : list nat := nil. (* not doable: Definition x := nil *)
@sth
Use the explicit version of sth.

We can do the same proof:

Notation "x :: y" := (cons x y)
                     (at level 60, right associativity).

Notation "[ ]" := nil.

Notation "[ x ; .. ; y ]" := (cons x .. (cons y []) ..).

Notation "x ++ y" := (app x y)
                     (at level 60, right associativity).


Theorem app_assoc:
  forall X (lst1 lst2 lst3 : list X),
    lst1 ++ lst2 ++ lst3 = (lst1 ++ lst2) ++ lst3.
Proof.
  intros.
  induction lst1 as [| h1 t1].
  simpl.
  reflexivity.
  simpl.
  rewrite IHt1.
  reflexivity.
Qed.
Polymorphic pairs
Inductive prod (X Y: Type) : Type :=
  | pair (x: X)(y: Y).

Arguments pair {X}{Y}.

Notation "( x , y )" := (pair x y).

Notation "X * Y" := (prod X Y) : type_scope.
Fixpoint zip {X Y} (lx: list X)(ly: list Y) : list (X*Y) :=
  match lx, ly with
  | [], _ => []
  | _, [] => []
  | hx :: tx, hy :: ty => (hx, hy) :: (zip tx ty)
  end.

The [] on the left hand side is not the same as the one on the right hand side. They refer to empty lists of corresponding types.

Compute zip [true;false] [3;4;5]. (* = [(true, 3); (false, 4)] : list (nat * nat)*)
Polymorphic options
Module PolyOption.

Inductive option (X : Type) : Type :=
  | Some (x: X)
  | None.

Arguments Some {X}.
Arguments None {X}.

End PolyOption.
Fixpoint nth_error {X}(l: list X)(n: nat) : option X :=
  match l with
  | [] => None
  | a :: l' => match n with
               | O => Some a
               | S n' => nth_error l' n'
               end
  end.

5.6. Higher-order Functions

Higher-order functions
functions that return functions.

E.g., functions that take multiple inputs and produce one output are actually higher-order functions, because they are considered as composition of single-variable functions (see "currying").

Definition simple_add (a b: nat) : nat :=
  a + b.

Check simple_add. (* : nat -> nat -> nat *)
use function as argument
Definition applythrice {X}(f: X -> X) (n: X) : X :=
  f(f (f n)).

E.g., filter (retain) list elements by a function,

Fixpoint filter {X}(test: X -> bool)(l: list X) : list X :=
  match l with
  | [] => []
  | h :: t =>
      if test h then h :: (filter test t)
      else filter test t
  end.

E.g.,

Definition countodd (l : list nat) : nat :=
  length (filter odd l).

E.g., map a list of elements using a given function,

Fixpoint map {X Y}(f: X->Y)(lst: list X) : list Y :=
  match lst with
  | [] => []
  | h :: t => (f h) :: (map f t)
  end.
Definition option_map {X Y}(f: X -> Y)(xo: option X) : option Y :=
match xo with
| None => None
| Some x => Some (f x)
end.

E.g., fold a list to a single element,

Fixpoint fold {X Y} (f: X -> Y -> Y) (lst: list X) (b: Y) : Y :=
  match lst with
  | [] => b
  | h :: t => f h (fold f t b)
  end.
Lambda expression, fun
function without a name
Definition mysquare(n : nat) : nat :=
  n * n.
Check mysquare. (* : nat -> nat *)
Check (fun n => n * n). (* : nat -> nat *)
Compute (fun n => n * n) 2. (* 4 *)

E.g., return a constant function

Definition constfun {X}(x: X) : nat -> X :=
  fun (k: nat) => x.

6. Inductive Proposition

Inductive <name> : <type> -> Prop :=
Inductively define a proposition on values of type <type>.

E.g., a proposition about parity,

Inductive ev : nat -> Prop :=
  | ev_0 : ev 0
  | ev_SS (n : nat): ev n -> ev (S (S n)).

For every n : nat, ev n is a proposition (supposed to indicate whether n is even). The constructors are rules that indirectly define ev n.

Theorem ev4 : ev 4.
Proof.
  assert(ev 0).
  apply ev_0.
  assert(ev 0 -> ev 2).
  apply ev_SS with (n:=0).
  apply H0 in H.
  assert(ev 2 -> ev 4).
  apply ev_SS with (n:=2).
  apply H1 in H.
  assumption.
Qed.

Or

Theorem ev4' : ev 4.
Proof.
  repeat apply ev_SS ; apply ev_0.
Qed.

Alternative definition

Inductive ev' : nat -> Prop :=
  | ev'_0 : ev' 0
  | ev'_SS (n : nat) (H : ev' n) : ev' (S (S n)).
Inductive <name> : <type> -> <type> -> Prop
Can have more arguments.

E.g., given a relation function R, the proposition indicates whether there is a transitive closure from x to y,

Inductive transitive_closure {X: Type} (R: X->X->Prop) :
  X->X->Prop :=
  | t_step (x y : X) : R x y -> transitive_closure R x y
  | t_trans (x y z: X) : transitive_closure R x y ->
                         transitive_closure R y z ->
                         transitive_closure R x z.

E.g.,

Module M.

Inductive le : nat -> nat -> Prop :=
| le_n (n : nat) : le n n
| le_S (n m : nat) (H: le n m) : le n (S m).

Notation "n <= m" := (le n m).

Definition lt (n m : nat) := le (S n) m.

Notation "n < m" := (lt n m).

End M.

6.1. Regular Expression Example

Reserved Notation
Define a notation before the definition of the function. It can then be used in the definition of the function (for tidiness).
where <notation> :=
add this at the end of the function definition.
Module RegExp.

Inductive reg_exp (T : Type) : Type :=
  | EmptySet
  | EmptyStr
  | Char (t : T)
  | App (r1 r2 : reg_exp T)
  | Union (r1 r2 : reg_exp T)
  | Star (r : reg_exp T).

Arguments EmptySet {T}.
Arguments EmptyStr {T}.
Arguments Char {T}.
Arguments App {T}.
Arguments Union {T}.
Arguments Star {T}.

Reserved Notation "s =~ re" (at level 80).

Inductive exp_match {T} : list T -> reg_exp T -> Prop :=
  | MEmpty : [] =~ EmptyStr
  | MChar x : [x] =~ (Char x)
  | MApp s1 re1 s2 re2
             (H1 : s1 =~ re1)
             (H2 : s2 =~ re2)
           : (s1 ++ s2) =~ (App re1 re2)
  | MUnionL s1 re1 re2
                (H1 : s1 =~ re1)
              : s1 =~ (Union re1 re2)
  | MUnionR re1 s2 re2
                (H2 : s2 =~ re2)
              : s2 =~ (Union re1 re2)
  | MStar0 re : [] =~ (Star re)
  | MStarApp s1 s2 re
                 (H1 : s1 =~ re)
                 (H2 : s2 =~ (Star re))
               : (s1 ++ s2) =~ (Star re)

  where "s =~ re" := (exp_match s re).

End RegExp.

6.2. Evaluation Example

Reserved Notation "e '==>' n" (at level 90, left associativity).

Inductive aexp : Type :=
| ANum (n : nat)
| APlus (a1 a2 : aexp)
| AMinus (a1 a2 : aexp)
| AMult (a1 a2 : aexp)
| ADiv (a1 a2 : aexp).

Inductive aevalR : aexp -> nat -> Prop :=
  | E_ANum (n : nat) :
      (ANum n) ==> n
  | E_APlus (e1 e2 : aexp) (n1 n2 : nat) :
      (e1 ==> n1) ->
      (e2 ==> n2) ->
      (APlus e1 e2) ==> (n1 + n2)
  | E_AMinus (e1 e2 : aexp) (n1 n2 : nat) :
      (e1 ==> n1) ->
      (e2 ==> n2) ->
      (AMinus e1 e2) ==> (n1 - n2)
  | E_AMult (e1 e2 : aexp) (n1 n2 : nat) :
      (e1 ==> n1) ->
      (e2 ==> n2) ->
      (AMult e1 e2) ==> (n1 * n2)
  | E_ADiv (a1 a2 : aexp) (n1 n2 n3 n4 : nat) :
      (a1 ==> n1) -> (a2 ==> n2) -> (n2 > 0) ->
      (mult n2 n3 + n4 =? n1 = true) -> (n4 < n2) -> (ADiv a1 a2) ==> n3

  where "e '==>' n" := (aevalR e n) : type_scope.

6.3. State Example

A total_map is a valuation. And a state is a total_map that maps to nat.

Module States.

From Coq Require Import Arith.Arith.
From Coq Require Import Bool.Bool.
Require Export Coq.Strings.String.
From Coq Require Import Logic.FunctionalExtensionality.
From Coq Require Import Lists.List.
Import ListNotations.


Definition total_map (A : Type) := string -> A.

Definition t_empty {A: Type} (default : A) : total_map A := (fun _ => default).

Definition t_update {A: Type} (m: total_map A)(x: string)(v: A) :=
   fun x' => if eqb x x' then v else m x'.


Notation "x '!->' v ';' m" := (t_update m x v)
                              (at level 100, v at next level, right associativity).
Notation "x '!->' v" := (x !-> v ; t_empty 0) (at level 100).


Definition state := total_map nat.

Inductive aexp : Type :=
  | ANum (n : nat)

  | AId (x : string)

  | APlus (a1 a2 : aexp)
  | AMinus (a1 a2 : aexp)
  | AMult (a1 a2 : aexp).

Inductive bexp : Type :=
  | BTrue
  | BFalse
  | BEq (a1 a2 : aexp)
  | BNeq (a1 a2 : aexp)
  | BLe (a1 a2 : aexp)
  | BGt (a1 a2 : aexp)
  | BNot (b : bexp)
  | BAnd (b1 b2 : bexp).
Corecion <cons> : <arg type> >-> <type>
If <cons> is a constructor with an argument of type <arg type> in the definition of type <type>, then whenever seeing a variable of type <arg type>, use <cons> to convert is to one of type <type>.
Coercion AId : string >-> aexp.
Coercion ANum : nat >-> aexp.
Scope
Declare Custom Entry <custom name>.
Declare Scope <scope name>.

Notation "<{ e }>" := e (at level 0, e custom <custom name> at level 99) : <scope name>.

Notation "<notation>" := <expression> (in custom <custom name>) : <scope name>.

Open Scope com_scope.

This means that, whenever some expression is enclosed by <{}>, it is a entry <custom name> in scope <scope name>. The <notation> in <custom name> is also in scope <scope name> and represents <expression>.

Declare Custom Entry com.
Declare Scope com_scope.

Notation "<{ e }>" := e (at level 0, e custom com at level 99) : com_scope.

Notation "( x )" := x (in custom com, x at level 99) : com_scope.
Notation "x" := x (in custom com at level 0, x constr at level 0) : com_scope.
Notation "f x .. y" := (.. (f x) .. y)
                  (in custom com at level 0, only parsing,
                  f constr at level 0, x constr at level 9,
                  y constr at level 9) : com_scope.
Notation "x + y" := (APlus x y) (in custom com at level 50, left associativity).
Notation "x - y" := (AMinus x y) (in custom com at level 50, left associativity).
Notation "x * y" := (AMult x y) (in custom com at level 40, left associativity).
Notation "'true'" := true (at level 1).
Notation "'true'" := BTrue (in custom com at level 0).
Notation "'false'" := false (at level 1).
Notation "'false'" := BFalse (in custom com at level 0).
Notation "x <= y" := (BLe x y) (in custom com at level 70, no associativity).
Notation "x > y" := (BGt x y) (in custom com at level 70, no associativity).
Notation "x = y" := (BEq x y) (in custom com at level 70, no associativity).
Notation "x <> y" := (BNeq x y) (in custom com at level 70, no associativity).
Notation "x && y" := (BAnd x y) (in custom com at level 80, left associativity).
Notation "'~' b" := (BNot b) (in custom com at level 75, right associativity).
Open Scope com_scope.

Definition W : string := "W".
Definition X : string := "X".
Definition Y : string := "Y".
Definition Z : string := "Z".

Check <{ 3 + (X * 2) }>. (* : aexp *)

Arithmetic evaluation and Boolean evaluation:

Fixpoint aeval (st : state) (a : aexp) : nat :=
  match a with
  | ANum n => n
  | AId x => st x
  | <{a1 + a2}> => (aeval st a1) + (aeval st a2)
  | <{a1 - a2}> => (aeval st a1) - (aeval st a2)
  | <{a1 * a2}> => (aeval st a1) * (aeval st a2)
  end.

Fixpoint beval (st : state) (b : bexp) : bool :=
  match b with
  | <{true}> => true
  | <{false}> => false
  | <{a1 = a2}> => (aeval st a1) =? (aeval st a2)
  | <{a1 <> a2}> => negb ((aeval st a1) =? (aeval st a2))
  | <{a1 <= a2}> => (aeval st a1) <=? (aeval st a2)
  | <{a1 > a2}> => negb ((aeval st a1) <=? (aeval st a2))
  | <{~ b1}> => negb (beval st b1)
  | <{b1 && b2}> => andb (beval st b1) (beval st b2)
  end.

6.4. (continued) WHILE Language Example

Inductive com : Type :=
  | CSkip
  | CAsgn (x : string) (a : aexp)
  | CSeq (c1 c2 : com)
  | CIf (b : bexp) (c1 c2 : com)
  | CWhile (b : bexp) (c : com).

(* Let's define notation *)

Notation "'skip'" :=
         CSkip (in custom com at level 0) : com_scope.
Notation "x := y" :=
         (CAsgn x y)
            (in custom com at level 0, x constr at level 0,
             y at level 85, no associativity) : com_scope.
Notation "x ; y" :=
         (CSeq x y)
           (in custom com at level 90, right associativity) : com_scope.
Notation "'if' x 'then' y 'else' z 'end'" :=
         (CIf x y z)
           (in custom com at level 89, x at level 99,
            y at level 99, z at level 99) : com_scope.
Notation "'while' x 'do' y 'end'" :=
         (CWhile x y)
            (in custom com at level 89, x at level 99, y at level 99) : com_scope.

E.g.,

Definition subtract_slowly_body : com :=
  <{ Z := Z - 1 ;
     X := X - 1 }>.

Definition subtract_slowly : com :=
  <{ while X <> 0 do
       subtract_slowly_body
     end }>.

6.4.1. Denotational semantics with no while

Fixpoint ceval_no_while (st : state) (c : com) : state :=
  match c with
    | <{ skip }> =>
        st
    | <{ x := a }> =>
        t_update st x (aeval st a)
    | <{ c1 ; c2 }> =>
        let st' := ceval_no_while st c1 in
        ceval_no_while st' c2
    | <{ if b then c1 else c2 end}> =>
        if (beval st b)
          then ceval_no_while st c1
          else ceval_no_while st c2
    | <{ while b do c end }> =>
        st (* Fixpoint? *)
  end.

6.4.2. Operational semantics (also supports while)

Reserved Notation
         "st '=[' c ']=>' st'"
         (at level 40, c custom com at level 99,
          st constr, st' constr at next level).

Inductive ceval : com -> state -> state -> Prop :=
  | E_Skip : forall st,
      st =[ skip ]=> st
  | E_Asgn : forall st a n x,
      aeval st a = n ->
      st =[ x := a ]=> (x !-> n ; st)
  | E_Seq : forall c1 c2 st st' st'',
      st =[ c1 ]=> st' ->
      st' =[ c2 ]=> st'' ->
      st =[ c1 ; c2 ]=> st''
  | E_IfTrue : forall st st' b c1 c2,
      beval st b = true ->
      st =[ c1 ]=> st' ->
      st =[ if b then c1 else c2 end]=> st'
  | E_IfFalse : forall st st' b c1 c2,
      beval st b = false ->
      st =[ c2 ]=> st' ->
      st =[ if b then c1 else c2 end]=> st'
  | E_WhileFalse : forall b st c,
      beval st b = false ->
      st =[ while b do c end ]=> st
    | E_WhileTrue : forall st st' st'' b c,
      beval st b = true ->
      st =[ c ]=> st' ->
      st' =[ while b do c end ]=> st'' ->
      st =[ while b do c end ]=> st''

  where "st =[ c ]=> st'" := (ceval c st st').

Small-step operational semantics

Notation "x '!->' v ';' m" := (t_update m x v)
                              (at level 100, v at next level, right associativity).
Notation "x '!->' v" := (x !-> v ; t_empty 0) (at level 100).
Inductive one_step : state -> com -> state -> com -> Prop :=
  | R_Skip (st: state)(P: com):
     one_step st <{skip ; P}> st <{P}>
  | R_Assgn (st:state)(x: string)(a: aexp)(n: nat):
     aeval st a = n ->
     one_step st <{ x := a }> (x !-> n ; st)  <{skip}>
  | R_Seq (st st': state)(P P' Q: com):
     one_step st P st' P' ->
     one_step st <{P; Q}> st' <{ P'; Q }>
  | R_IfTrue (st: state)(b: bexp)(P Q: com):
    beval st b = true ->
    one_step st <{ if b then P else Q end }> st P
  | R_IfFalse (st:state)(b:bexp)(P Q: com):
    beval st b = false ->
    one_step st <{ if b then P else Q end }> st Q
  | R_WhileTrue (st: state)(b: bexp)(P: com):
    beval st b = true ->
    one_step st <{ while b do P end }> st <{ P ; while b do P end }>
  | R_WhileFalse (st:state)(b: bexp)(P: com):
    beval st b = false ->
    one_step st <{ while b do P end }> st <{ skip }>.

Notation " ( st | c ) ~> ( st' | c' ) " := (one_step st c st' c')(at level 40).

Transitive closure

Inductive multi_step : state -> com -> state -> com -> Prop :=
  | R_zero (st : state)(P: com):
  | R_Base (st st': state)(P P': com):
     one_step st P st' P' -> multi_step st P st' P'
  | R_Tran (st st' st'': state)(P P' P'': com):
      one_step st P st' P' /\ multi_step st' P' st'' P'' ->
      multi_step st P st'' P''.


Notation " ( st | c ) ~~> ( st' | c' ) " := (multi_step st c st' c')(at level 40).

6.5. Hoare Logic

Given an expression a, replace x in it with e:

Fixpoint repl_aexp(a: aexp)(x: string)(e: aexp) : aexp :=
  match a with
  | ANum n => a
  | AId x' => if eqb x x' then e else x'
  | <{a1 + a2}> =>
      let a1' := repl_aexp a1 x e in
      let a2' := repl_aexp a2 x e in
      <{a1' + a2'}>
  | <{a1 - a2}> =>
      let a1' := repl_aexp a1 x e in
      let a2' := repl_aexp a2 x e in
      <{a1' - a2'}>
  | <{a1 * a2}> =>
      let a1' := repl_aexp a1 x e in
      let a2' := repl_aexp a2 x e in
      <{a1' * a2'}>
  end.

Given a boolean expression a, replace x in it with e:

Fixpoint repl(a: bexp)(x: string)(e: aexp) : bexp :=
  match a with
  | <{true}> => <{true}>
  | <{false}> => <{false}>
  | <{a1 = a2}> =>
    let a1' := repl_aexp a1 x e in
    let a2' := repl_aexp a2 x e in
      <{a1' = a2'}>
  | <{a1 <> a2}> =>
    let a1' := repl_aexp a1 x e in
    let a2' := repl_aexp a2 x e in
      <{a1' <> a2'}>
  | <{a1 <= a2}> =>
    let a1' := repl_aexp a1 x e in
    let a2' := repl_aexp a2 x e in
      <{a1' <= a2'}>
  | <{a1 > a2}> =>
    let a1' := repl_aexp a1 x e in
    let a2' := repl_aexp a2 x e in
      <{a1' > a2'}>
  | <{~ b1}> =>
    let b1' := repl b1 x e in
      <{~ b1'}>
  | <{b1 && b2}> =>
    let b1' := repl b1 x e in
    let b2' := repl b2 x e in
      <{b1' && b2'}>
  end.
Definition entails(b1 b2: bexp): Prop :=
  forall st, beval st b1 = true -> beval st b2 = true.

Notation " a -.> b " := (entails a b)(at level 40).

Inductive hoare: bexp -> com -> bexp -> Prop :=
  | H_Skip (a: bexp):
    hoare a <{skip}> a
  | H_Assgn (a: bexp)(x: string)(e: aexp):
    hoare (repl a x e) <{ x := e }> a
  | H_Seq (a b c: bexp)(P Q: com):
    hoare a P c -> hoare c Q b ->
    hoare a <{ P ; Q }> b
  | H_If (a g b: bexp)(P Q: com):
    hoare <{ a && g }> P b ->
    hoare <{ a && ~(g)}> Q b ->
    hoare a <{if g then P else Q end}> b
  | H_Conseq (a a' b b': bexp)(P: com):
    a' -.> a ->
    b -.> b' ->
    hoare a P b ->
    hoare a' P b'
  | H_while (a b: bexp)(P: com):
    hoare <{a && b}> P a ->
    hoare a <{ while b do P end }> <{~b && a}>.

Notation " [[ a ]] P [[ b ]] " := (hoare a P b)(at level 40).