Coq
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:
- VScode with vscoq:
- Emacs with proof-general (customization doc) and company-coq.
Or
Standalone interfaces: the CoqIDE alongside Coq.
2. Basic Usage
Every statement must end with .
.
Section:
- Begins with
Section <name>.
, ends withEnd <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>
.
- multiple variables
2.2. Propositions
Propositions are of type Prop
.
Operations are:
- Implication
->
: Right associativity.p -> q -> p
meansp -> (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 withQed.
(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 asx
(otherwise up to Coq)intro x eq
: … and specify the name of hypothesis onx
aseq
(otherwiseH
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 asn
.
3.2. assumption
assumption
looks for a hypothesis which matches with the goal and prove the goal.
; assumption
: applyassumption
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 hypothesisH: p -> q
, then this command does backward reasoning the current goal becomes the premisep
. - It also separate more subgoals if
H
is a multi-hop implication, e.g., ifH: p -> r -> q
then the subgoals becomep
andq
. - (like
rewrite
) If the current goal andH
are both equations, the command substitute (one side of) the goal equation usingH
(based on pattern matching). apply thm with (m := c)
: apply a theoremthm
with variablem
in the theorem matched with variablec
(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
: Seein H
.
- If the current goal is
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 hypothesisH0: p
andH1: 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 formH: p /\ q
.n destruct x as [x1 | x2 | ...]
: Destruct a variablex
into multiple cases based on the constructors of the type ofx
, with each case named as the corresponding variable in[]
separated by|
. If a constructor of the type ofx
consists of multiple arguments, list those arguments like[... | a1 a2 | ...]
. See 5.1.1destruct x as [...] eqn:E
: Additionally put the equation ofx
for the current case into hypothesisE
.
- If
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 asH0
.
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 isf n = f m
, wheref
is a function (including constructor), then this proves the goal.
3.12. in H
<tactic> in H
- Apply tactics to hypothesis.
simpl in H
: simplyH
symmetry in H
:apply H in H0 as H1
: Forward reasonH0
usingH
and let the result beH1
. 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 ofn
, likeforall n : nat, <hypothesis> -> goal
.
3.14. Ltac
- Ltac <name> H :=~
- Define a tactics named
<name>
with argumentH
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 equationn = <expression>
will be added to hypotheses and instances of<expression>
will be substituted byn
.
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.
- 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).