18 Basic Program Syntax
This chapter is adapted from Formal Reasoning About Programs, Chapter 2: Basic Program Syntax, by Adam Chlipala. Because we are using only a single chapter from the original work, we have modified it to remove dependencies on the FRAP libraries. However, the license of the original book does not permit the creation or distribution of modified versions. Accordingly, please do not distribute this material.
Rocq
From Coq Require Import Lia. From Coq Require Import Arith. Module ArithWithConstants.
Rocq
Inductive arith : Set := | Const (n : nat) | Plus (e1 e2 : arith) | Times (e1 e2 : arith).
Rocq
Example ex1 := Const 42. Example ex2 := Plus (Const 1) (Times (Const 2) (Const 3)).
Rocq
Fixpoint size (e : arith) : nat := match e with | Const _ => 1 | Plus e1 e2 => 1 + size e1 + size e2 | Times e1 e2 => 1 + size e1 + size e2 end.
Rocq
Compute size ex1. Compute size ex2.
Rocq
Fixpoint depth (e : arith) : nat := match e with | Const _ => 1 | Plus e1 e2 => 1 + max (depth e1) (depth e2) | Times e1 e2 => 1 + max (depth e1) (depth e2) end. Compute depth ex1. Compute depth ex2.
Rocq
Theorem depth_le_size : forall e, depth e <= size e. Proof.
Rocq
induction e.
Rocq
- simpl.
Rocq
lia.
Rocq
- simpl. lia. - simpl. lia. Qed.
Rocq
Theorem depth_le_size_snazzy : forall e, depth e <= size e. Proof. induction e; simpl; lia.
Rocq
Qed.
Rocq
Fixpoint commuter (e : arith) : arith := match e with | Const _ => e | Plus e1 e2 => Plus (commuter e2) (commuter e1) | Times e1 e2 => Times (commuter e2) (commuter e1) end. Compute commuter ex1. Print ex2. Compute commuter ex2.
Rocq
Theorem size_commuter : forall e, size (commuter e) = size e. Proof. induction e; simpl; lia. Qed. Theorem depth_commuter : forall e, depth (commuter e) = depth e. Proof. induction e; simpl; lia. Qed.
Rocq
Lemma max_comm2: forall (n m:nat), max n m = max m n. Proof. induction n, m. - simpl. trivial. - simpl. trivial. - simpl. trivial. - rewrite <- Nat.succ_max_distr. rewrite <- Nat.succ_max_distr. f_equal. rewrite IHn. trivial. Qed. Theorem depth_commuter' : forall e, depth (commuter e) = depth e. Proof. induction e. - simpl. trivial. - simpl. rewrite IHe1. rewrite IHe2. f_equal. rewrite max_comm2. trivial. - simpl. rewrite IHe1. rewrite IHe2. f_equal. rewrite max_comm2. trivial. Qed. Theorem commuter_inverse : forall e, commuter (commuter e) = e. Proof. (* induction e. - simpl. trivial. - simpl. rewrite IHe1. rewrite IHe2. trivial. - simpl. rewrite IHe1. rewrite IHe2. trivial. *) induction e; simpl; (try rewrite IHe1; try rewrite IHe2); trivial. Qed. End ArithWithConstants.
Rocq
From Coq Require Import Arith Lia String. Module ArithWithVariables. Notation var := string. Inductive arith : Set := | Const (n : nat) | Var (x : var) (* <-- this is the new constructor! *) | Plus (e1 e2 : arith) | Times (e1 e2 : arith). Example ex1 := Const 42. Example ex2 := Plus (Const 1) (Times (Var "x") (Const 3)). Fixpoint size (e : arith) : nat := match e with | Const _ => 1 | Var _ => 1 | Plus e1 e2 => 1 + size e1 + size e2 | Times e1 e2 => 1 + size e1 + size e2 end. Compute size ex1. Compute size ex2. Fixpoint depth (e : arith) : nat := match e with | Const _ => 1 | Var _ => 1 | Plus e1 e2 => 1 + max (depth e1) (depth e2) | Times e1 e2 => 1 + max (depth e1) (depth e2) end. Compute depth ex1. Compute depth ex2. Theorem depth_le_size : forall e, depth e <= size e. Proof. induction e; simpl; lia. Qed. Fixpoint commuter (e : arith) : arith := match e with | Const _ => e | Var _ => e | Plus e1 e2 => Plus (commuter e2) (commuter e1) | Times e1 e2 => Times (commuter e2) (commuter e1) end. Compute commuter ex1. Compute commuter ex2. Theorem size_commuter : forall e, size (commuter e) = size e. Proof. induction e; simpl; lia. Qed. Theorem depth_commuter : forall e, depth (commuter e) = depth e. Proof. induction e; simpl; lia. Qed. Theorem commuter_inverse : forall e, commuter (commuter e) = e. Proof. (* induction e. - simpl. trivial. - simpl. trivial. - simpl. rewrite IHe1. rewrite IHe2. trivial. - simpl. rewrite IHe1. rewrite IHe2. trivial. *) induction e; simpl; (try rewrite IHe1; try rewrite IHe2); trivial. Qed.
Rocq
Infix "==" := string_dec (at level 70). Fixpoint substitute (inThis : arith) (replaceThis : var) (withThis : arith) : arith := match inThis with | Const _ => inThis | Var x => if x == replaceThis then withThis else inThis | Plus e1 e2 => Plus (substitute e1 replaceThis withThis) (substitute e2 replaceThis withThis) | Times e1 e2 => Times (substitute e1 replaceThis withThis) (substitute e2 replaceThis withThis) end. (* An intuitive property about how much [substitute] might increase depth. *) Theorem substitute_depth : forall replaceThis withThis inThis, depth (substitute inThis replaceThis withThis) <= depth inThis + depth withThis. Proof. induction inThis; simpl. (* [destruct e]: break the proof into one case for each constructor that might have * been used to build the value of expression [e]. In the special case where * [e] essentially has a Boolean type, we consider whether [e] is true or false. *) - lia. - destruct (x == replaceThis); simpl. + lia. + lia. - lia. - lia. Qed.
Rocq
Theorem substitute_depth_snazzy : forall replaceThis withThis inThis, depth (substitute inThis replaceThis withThis) <= depth inThis + depth withThis. Proof. induction inThis; simpl; try match goal with | [ |- context[if ?a == ?b then _ else _] ] => destruct (a == b); simpl end; lia. (*try match goal with | [ |- context[if ?a ==v ?b then _ else _] ] => cases (a ==v b); simplify end; linear_arithmetic.*) Qed.
Rocq
Theorem substitute_self : forall replaceThis inThis, substitute inThis replaceThis (Var replaceThis) = inThis. Proof. induction inThis; simpl; try match goal with | [ |- context[if ?a == ?b then _ else _] ] => destruct (a == b); simpl end; congruence. Qed.
Rocq
Theorem substitute_commuter : forall replaceThis withThis inThis, commuter (substitute inThis replaceThis withThis) = substitute (commuter inThis) replaceThis (commuter withThis). Proof. induction inThis; simpl; try match goal with | [ |- context[if ?a == ?b then _ else _] ] => destruct (a == b); simpl end; congruence. Qed.
Rocq
Fixpoint constantFold (e : arith) : arith := match e with | Const _ => e | Var _ => e | Plus e1 e2 => let e1' := constantFold e1 in let e2' := constantFold e2 in match e1', e2' with | Const n1, Const n2 => Const (n1 + n2) | Const 0, _ => e2' | _, Const 0 => e1' | _, _ => Plus e1' e2' end | Times e1 e2 => let e1' := constantFold e1 in let e2' := constantFold e2 in match e1', e2' with | Const n1, Const n2 => Const (n1 * n2) | Const 1, _ => e2' | _, Const 1 => e1' | Const 0, _ => Const 0 | _, Const 0 => Const 0 | _, _ => Times e1' e2' end end.
Rocq
Theorem size_constantFold : forall e, size (constantFold e) <= size e. Proof. induction e; simpl; repeat match goal with | [ |- context[match ?E with _ => _ end] ] => destruct E; simpl in * end; lia. (*repeat match goal with | [ |- context[match ?E with _ => _ end] ] => cases E; simplify end; linear_arithmetic.*) Qed. Business as usual, with another commuting law Theorem commuter_constantFold : forall e, commuter (constantFold e) = constantFold (commuter e). Proof. induction e; simpl; repeat match goal with | [ |- context[match ?E with _ => _ end] ] => destruct E; simpl in * | [ H : ?f _ = ?f _ |- _ ] => injection H as H; subst | [ |- ?f _ = ?f _ ] => f_equal end; try (lia || congruence). Qed.
f_equal: when the goal is an equality between two applications of the same function, switch to proving that the function arguments are pairwise equal.
invert H: replace hypothesis [H] with other facts that can be deduced from the structure of [H]’s statement. This is admittedly a fuzzy description for now; we’ll learn much more about the logic shortly! Here, what matters is that, when the hypothesis is an equality between two applications of a constructor of an inductive type, we learn that the arguments to the constructor must be pairwise equal.
ring: prove goals that are equalities over some registered ring or semiring, in the sense of algebra, where the goal follows solely from the axioms of that algebraic structure.
Rocq
Definition isConst (e : arith) : option nat := match e with | Const n => Some n | _ => None end.
Rocq
Fixpoint pushMultiplicationInside' (multiplyBy : nat) (e : arith) : arith := match e with | Const n => Const (multiplyBy * n) | Var _ => Times (Const multiplyBy) e | Plus e1 e2 => Plus (pushMultiplicationInside' multiplyBy e1) (pushMultiplicationInside' multiplyBy e2) | Times e1 e2 => match isConst e1 with | Some k => pushMultiplicationInside' (k * multiplyBy) e2 | None => Times (pushMultiplicationInside' multiplyBy e1) e2 end end.
Rocq
Definition pushMultiplicationInside (e : arith) : arith := pushMultiplicationInside' 1 e.
Rocq
Lemma n_times_0 : forall n, n * 0 = 0. Proof. lia. Qed.
Rocq
Lemma depth_pushMultiplicationInside'_irrelevance0 : forall e multiplyBy, depth (pushMultiplicationInside' multiplyBy e) = depth (pushMultiplicationInside' 0 e). Proof. induction e; intros; simpl in *. lia. lia. rewrite IHe1. (* [rewrite H]: where [H] is a hypothesis or previously proved theorem, * establishing [forall x1 .. xN, e1 = e2], find a subterm of the goal * that equals [e1], given the right choices of [xi] values, and replace * that subterm with [e2]. *) rewrite IHe2. lia. destruct (isConst e1); simpl. + rewrite IHe2. (* .unfold *) rewrite Nat.mul_0_r. lia. + rewrite IHe1. lia. Qed.
Rocq
Lemma depth_pushMultiplicationInside'_irrelevance0_snazzy : forall e multiplyBy, depth (pushMultiplicationInside' multiplyBy e) = depth (pushMultiplicationInside' 0 e). Proof. induction e; simpl; try match goal with | [ |- context[match ?E with _ => _ end] ] => destruct E; simpl end; congruence. Qed.
Rocq
Lemma depth_pushMultiplicationInside'_irrelevance : forall e multiplyBy1 multiplyBy2, depth (pushMultiplicationInside' multiplyBy1 e) = depth (pushMultiplicationInside' multiplyBy2 e). Proof. intros. transitivity (depth (pushMultiplicationInside' 0 e)). (* [transitivity X]: when proving [Y = Z], switch to proving [Y = X] * and [X = Z]. *) apply depth_pushMultiplicationInside'_irrelevance0. (* [apply H]: for [H] a hypothesis or previously proved theorem, * establishing some fact that matches the structure of the current * conclusion, switch to proving [H]'s own hypotheses. * This is *backwards reasoning* via a known fact. *) symmetry. (* [symmetry]: when proving [X = Y], switch to proving [Y = X]. *) apply depth_pushMultiplicationInside'_irrelevance0. Qed.
Rocq
Lemma depth_pushMultiplicationInside' : forall e, depth (pushMultiplicationInside' 0 e) <= S (depth e). Proof. induction e; simpl. lia. lia. lia. destruct (isConst e1); simpl. rewrite n_times_0. lia. lia. Qed. Local Hint Rewrite n_times_0. Registering rewrite hints will get [simplify] to apply them for us automatically! #[local] Hint Rewrite Nat.mul_0_r : arith. Lemma depth_pushMultiplicationInside'_snazzy : forall e, depth (pushMultiplicationInside' 0 e) <= S (depth e). Proof. induction e; intros; simpl in *; try match goal with | [ |- context[match ?E with _ => _ end] ] => destruct E; simpl; autorewrite with arith end;lia. Qed. Theorem depth_pushMultiplicationInside : forall e, depth (pushMultiplicationInside e) <= S (depth e). Proof. intros. unfold pushMultiplicationInside. (* [unfold X]: replace [X] by its definition. *) rewrite depth_pushMultiplicationInside'_irrelevance0. apply depth_pushMultiplicationInside'. Qed. End ArithWithVariables.