Lecture 03

include some useful libraries
Require Import Bool.
Require Import List.
Require Import String.
Require Import Omega.

List provides the cons notation "::"
"x :: xs" is the same as "cons x xs"
Fixpoint my_length {A: Set} (l: list A) : nat :=
  match l with
  | nil => O
  | x :: xs => S (my_length xs)
  end.

List provides the append notation "++"
"xs ++ ys" is the same as "app xs ys"
Fixpoint my_rev {A: Set} (l: list A) : list A :=
  match l with
  | nil => nil
  | x :: xs => rev xs ++ x :: nil
  end.

some interesting types
Prop is the type of proofs Just like Set, we use it as a type for things Unlike Set, we mainly use it for the type of facts
myTrue is a proposition that holds It has one constructor with no arguments No matter the context, you an always make a value of type myTrue No matter the context, you can always prove myTrue (essentially True)
Inductive myTrue : Prop :=
| I : myTrue.

Lemma foo :
  myTrue.
Proof.
  constructor.
exact I.
Qed.

myFalse is the proposition that never holds It has no constructors, and there are no ways to prove myFalse No matter the context, myFalse doesn't hold
Inductive myFalse : Prop :=
.

Lemma bogus:
  False -> 1 = 2.
Proof.
  intros.
inversion does case analysis on a hypothesis for each constructor that could have constructed the hypothesis, we end up with a subgoal for each constructor for False (the builtin equivalent to myFalse), there are 0 constructors
  inversion H.

Qed.

Any type can be a proposition Thus we can prove any type Slightly weird, but it works
Lemma foo´:
  Set.
Proof.
  exact bool.
exact (list nat). exact nat.
Qed.

Lemma also_bogus:
  1 = 2 -> False.
Proof.
  intros.
discriminate is a tactic that looks for mismatching constructors under the hood, 1 looks like S (0) and 2 looks like S (S 0) It peels off one S, gets 0 = S 0 0 and S are different constructors, thus they are not equal
  discriminate.
Qed.

Note that even equality is defined, not builtin
Print eq.

What's wrong with this? There's no way to build any objects of type yo
Inductive yo : Prop :=
| yolo : yo -> yo.

We want to prove that no objects of type yo exist We can prove that any object of that type would mean False Thus there are none
Lemma yoyo:
  yo -> False.
Proof.
  intros.
  inversion H.
well, that didn't work
  induction H.
  assumption.
but that did!
Qed.

check out negation It looks just like what we just did
Print not.

Expression Syntax

Now let's build a programming language!
We can define parts of a language as an inductive datatype.
Inductive expr : Set :=
  
A constant expression, like "3" or "0"
| Const : nat -> expr
  
A program variable, like "x" or "foo"
| Var : string -> expr
  
Adding expressions
| Add : expr -> expr -> expr
  
Multiplying expressions
| Mul : expr -> expr -> expr
  
Comparing expressions
| Cmp : expr -> expr -> expr.

On paper, this would be written as a "BNF grammar" as:
    expr ::= N
          |  V
          |  expr <+> expr
          |  expr <*> expr
          |  expr <?> expr
Coq provides mechanism to define your own notation which we can use to get "concrete syntax" Feel free to ignore most of this, especially the stuff farther right
Notation "´C´ X" := (Const X) (at level 80).
Notation "´V´ X" := (Var X) (at level 81).
Notation "X <+> Y" := (Add X Y) (at level 83, left associativity).
Notation "X <*> Y" := (Mul X Y) (at level 82, left associativity).
Notation "X <?> Y" := (Cmp X Y) (at level 84, no associativity).

parsing is classic CS topic, but won't say much more while parsing is still an active research topic in some places, we know how to do it pretty well in a lot of cases
we can write functions to analyze expressions Here we're simply going to count the number of const subexpressions in a given expression
Fixpoint nconsts (e: expr) : nat :=
  match e with
  | Const _ => 1
same as S O
  | Var _ => 0
same as O
  | Add e1 e2 => nconsts e1 + nconsts e2
                 
same as plus (nconsts e1) (nconsts e2)
  | Mul e1 e2 => nconsts e1 + nconsts e2
  | Cmp e1 e2 => nconsts e1 + nconsts e2
  end.

Coq also provides existential quantifiers We prove them by providing concrete examples
Lemma expr_w_3_consts:
  exists e,
  nconsts e = 3.
Proof.
Here we give a concrete example
  exists (C 3 <+> C 2 <+> C 1).
Now we have to show that the example we gave satisfies the property
  simpl. reflexivity.
Qed.

Compute the size of an expression
Fixpoint esize (e: expr) : nat :=
  match e with
  | Const _ => 1
same as S O
  | Var _ => 1
  | Add e1 e2 => esize e1 + esize e2
                 
same as plus (esize e1) (esize e2)
  | Mul e1 e2 => esize e1 + esize e2
  | Cmp e1 e2 => esize e1 + esize e2
  end.

and do proofs about programs Show that we always have more nodes than consts
Lemma nconsts_le_size:
  forall e,
  nconsts e <= esize e.
Proof.
  intros.
  induction e.
  + simpl. auto.
auto will solve many simple goals auto will happily do nothing to your goal as well
  + simpl. auto.
omega will solve many arithemetic goals omega will only work if it solves your goal
  + simpl. omega.
  + simpl. omega.
  + simpl. omega.
Qed.

that proof had a lot of copy-pasta :(
Lemma nconsts_le_size´:
  forall e,
  nconsts e <= esize e.
Proof.
  intros.
Here we introduce the semicolon ; for any tactics a and b, "a; b" runs a, then runs b on all of the generated subgoals
do induction, then on every resulting subgoal do simpl, then on every resulting subgoal do auto, then on every resulting subgoal do omega
  induction e; simpl; auto; omega.
note that after the auto, only the Add, Mul, and Cmp subgoals remain, but it's hard to tell since the proof does not "pause"
Qed.

In order to figure out notation, use Locate
Locate "<=".
This generates a lot We care about the entry about nats
"n <= m" := le n m : nat_scope (default interpretation)
Now let's look at the definition
Print le.

it's a relation defined as an inductive predicate
we give rules for when the relation holds anything is less than itself and if something (n) was less than or equal to some other thing (m), then n <= S (m)
we can define our own relations to encode properties of expressions
Each of the constructors corresponds to how you can prove this fact

Inductive has_const : expr -> Prop :=
| hc_const :
    forall c, has_const (Const c)
| hc_add_l :
    forall e1 e2,
    has_const e1 ->
    has_const (Add e1 e2)
| hc_add_r :
    forall e1 e2,
    has_const e2 ->
    has_const (Add e1 e2)
| hc_mul_l :
    forall e1 e2,
    has_const e1 ->
    has_const (Mul e1 e2)
| hc_mul_r :
    forall e1 e2,
    has_const e2 ->
    has_const (Mul e1 e2)
| hc_cmp_l :
    forall e1 e2,
    has_const e1 ->
    has_const (Cmp e1 e2)
| hc_cmp_r :
    forall e1 e2,
    has_const e2 ->
    has_const (Cmp e1 e2).

Are add and mul commutative? Not as just syntax
Lemma add_comm :
  (forall e1 e2, Add e1 e2 = Add e2 e1) ->
  False.
Proof.
  intros.
specialize gives concrete arguments to hypotheses with forall
  specialize (H (Const 0) (Const 1)).
inversion is smart
  inversion H.
Qed.

Similarly, we can define a relation for having a variable
Inductive has_var : expr -> Prop :=
| hv_var :
    forall s, has_var (Var s)
| hv_add_l :
    forall e1 e2,
    has_var e1 ->
    has_var (Add e1 e2)
| hv_add_r :
    forall e1 e2,
    has_var e2 ->
    has_var (Add e1 e2)
| hv_mul_l :
    forall e1 e2,
    has_var e1 ->
    has_var (Mul e1 e2)
| hv_mul_r :
    forall e1 e2,
    has_var e2 ->
    has_var (Mul e1 e2)
| hv_cmp_l :
    forall e1 e2,
    has_var e1 ->
    has_var (Cmp e1 e2)
| hv_cmp_r :
    forall e1 e2,
    has_var e2 ->
    has_var (Cmp e1 e2).

we could write boolean functions to check the same properties orb is just or for booleans
Print orb.

Fixpoint hasConst (e: expr) : bool :=
  match e with
  | Const _ => true
  | Var _ => false
  | Add e1 e2 => orb (hasConst e1) (hasConst e2)
  | Mul e1 e2 => orb (hasConst e1) (hasConst e2)
  | Cmp e1 e2 => orb (hasConst e1) (hasConst e2)
  end.

the Bool library provides "||" as a notation for orb
Fixpoint hasVar (e: expr) : bool :=
  match e with
  | Const _ => false
  | Var _ => true
  | Add e1 e2 => hasVar e1 || hasVar e2
  | Mul e1 e2 => hasVar e1 || hasVar e2
  | Cmp e1 e2 => hasVar e1 || hasVar e2
  end.

That looks way easier! However, as the quarter progresses, we'll see that sometime defining a property as an inductive relation is more convenient
We can prove that our relational and functional versions agree This property is that the hasConst function is COMPLETE with respect to the relation Thus, anything that satisfies the relation evaluates to "true" with the function
Lemma has_const_hasConst:
  forall e,
  has_const e ->
  hasConst e = true.
Proof.
  intros.
  induction e.
  + simpl. reflexivity.
  + simpl.
uh oh, trying to prove something false! it's OK though because we have a bogus hyp!
    inversion H.
inversion lets us do case analysis on how a hypothesis of an inductive type may have been built. In this case, there is no way to build a value of type "has_const (Var s)", so we complete the proof of this subgoal for all zero ways of building such a value
  +
here we use inversion to consider how a value of type "has_const (Add e1 e2)" could have been built
    inversion H.
    -
built with hc_add_l
      subst.
subst rewrites all equalities it can
      apply IHe1 in H1.
      simpl.
remember notation "||" is same as orb
      rewrite H1. simpl. reflexivity.
    -
built with hc_add_r
      subst. apply IHe2 in H1.
      simpl. rewrite H1.
use fact that orb is commutative
      rewrite orb_comm.
you can find this by turning on auto completion or using a search query
SearchAbout orb.
      simpl. reflexivity.
  +
Mul case is similar
    inversion H; simpl; subst.
    - apply IHe1 in H1; rewrite H1; auto.
    - apply IHe2 in H1; rewrite H1;
      rewrite orb_comm; auto.
  +
Cmp case is similar
    inversion H; simpl; subst.
    - apply IHe1 in H1; rewrite H1; auto.
    - apply IHe2 in H1; rewrite H1;
      rewrite orb_comm; auto.
Qed.

now the other direction Here we'll prove that the hasConst function is SOUND with respect to the relation That if hasConst produces true, then there is some proof of the inductive relation
Lemma hasConst_has_const:
  forall e,
  hasConst e = true ->
  has_const e.
Proof.
  intros.
  induction e.
  + simpl.
we can prove this case with a constructor
    constructor.
this uses hc_const
  +
Uh oh, no constructor for has_const can possibly produce a value of our goal type! It's OK though because we have a bogus hypothesis.
    simpl in H.
    discriminate.
  +
now do Add case
    simpl in H.
either e1 or e2 had a Const
    apply orb_true_iff in H.
consider cases for H
    destruct H.
    -
e1 had a Const
      apply hc_add_l.
      apply IHe1.
      assumption.
    -
e2 had a Const
      apply hc_add_r.
      apply IHe2.
      assumption.
   +
Mul case is similar
     simpl in H; apply orb_true_iff in H; destruct H.
     -
constructor will just use hc_mul_l
       constructor. apply IHe1. assumption.
     -
constructor will screw up and try hc_mul_l again! constructor is rather dim
       constructor.
OOPS!
       Undo.
       apply hc_mul_r. apply IHe2. assumption.
   +
Cmp case is similar
     simpl in H; apply orb_true_iff in H; destruct H.
     - constructor; auto.
     - apply hc_cmp_r; auto.
Qed.

we can stitch all these together
Lemma has_const_iff_hasConst:
  forall e,
  has_const e <-> hasConst e = true.
Proof.
  intros. split.
  +
  • >
    apply has_const_hasConst.
  +
<-
    apply hasConst_has_const.
Qed.

all that was only for the true cases! can also use not and do the false cases
Here we prove it directly However, we could use has_const_iff_hasConst for a much more direct and simple proof
Lemma not_has_const_hasConst:
  forall e,
  ~ has_const e ->
  hasConst e = false.
Proof.
  unfold not. intros.
  induction e.
  + simpl.
uh oh, trying to prove something bogus better exploit a bogus hypothesis
    exfalso.
proof by contradiction
    apply H. constructor.
  + simpl. reflexivity.
  + simpl. apply orb_false_iff.
prove conjunction by proving left and right
    split.
    - apply IHe1. intro.
      apply H. apply hc_add_l. assumption.
    - apply IHe2. intro.
      apply H. apply hc_add_r. assumption.
  +
Mul case is similar
    simpl; apply orb_false_iff.
    split.
    - apply IHe1; intro.
      apply H. apply hc_mul_l. assumption.
    - apply IHe2; intro.
      apply H. apply hc_mul_r. assumption.
  +
Cmp case is similar
    simpl; apply orb_false_iff.
    split.
    - apply IHe1; intro.
      apply H. apply hc_cmp_l. assumption.
    - apply IHe2; intro.
      apply H. apply hc_cmp_r. assumption.
Qed.

Since we've proven the iff for the true case We can use it to prove the false case This is the same lemma as above, but using our previous results
Lemma not_has_const_hasConst´:
  forall e,
  ~ has_const e ->
  hasConst e = false.
Proof.
  intros.
do case analysis on hasConst e eqn:? remembers the result in a hypothesis
  destruct (hasConst e) eqn:?.
  *
    
now we have hasConst e = true in our hypothesis
    rewrite <- has_const_iff_hasConst in Heqb.

We have a contradiction in our hypotheses discriminate won't work this time though
    unfold not in H.
    apply H in Heqb.
    inversion Heqb.
  *
    
For the other case, this is easy
    reflexivity.
Qed.

Now the other direction of the false case
Lemma false_hasConst_hasConst:
  forall e,
  hasConst e = false ->
  ~ has_const e.
Proof.
  unfold not. intros.
  induction e;
    
crunch down everything in subgoals
    simpl in *.
  + discriminate.
  + inversion H0.
  + apply orb_false_iff in H.
get both proofs out of a conjunction by destructing it
    destruct H.
case analysis on H0 DISCUSS: how do we know to do this?
    inversion H0.
    - subst. auto.
auto will chain things for us
    - subst. auto.
  +
Mul case similar
    apply orb_false_iff in H; destruct H.
    inversion H0; subst; auto.
  +
Cmp case similar
    apply orb_false_iff in H; destruct H.
    inversion H0; subst; auto.
Qed.

Since we've proven the iff for the true case We can use it to prove the false case This is the same lemma as above, but using our previous results
Lemma false_hasConst_hasConst´:
  forall e,
  hasConst e = false ->
  ~ has_const e.
Proof.
  intros.
~ X is just X -> False
  unfold not.
  intros.
  rewrite has_const_iff_hasConst in H0.
  rewrite H in H0.
  discriminate.
Qed.

We can also do all the same sorts of proofs for has_var and hasVar

Lemma has_var_hasVar:
  forall e,
  has_var e ->
  hasVar e = true.
Proof.
TODO: try this without copying from above
Admitted.

Lemma hasVar_has_var:
  forall e,
  hasVar e = true ->
  has_var e.
Proof.
TODO: try this without copying from above
Admitted.

Lemma has_var_iff_hasVar:
  forall e,
  has_var e <-> hasVar e = true.
Proof.
TODO: try this without copying from above
Admitted.

we can also prove things about expressions
Lemma expr_bottoms_out:
  forall e,
  has_const e \/ has_var e.
Proof.
  intros. induction e.
  +
prove left side of disjunction
    left.
    constructor.
  +
prove right side of disjunction
    right.
    constructor.
  +
case analysis on IHe1
    destruct IHe1.
    - left. constructor. assumption.
    - right. constructor. assumption.
  +
Mul case similar
    destruct IHe1.
    - left. constructor. assumption.
    - right. constructor. assumption.
  +
Cmp case similar
    destruct IHe1.
    - left. constructor. assumption.
    - right. constructor. assumption.
Qed.

we could have gotten some of the has_const lemmas by being a little clever! (but then we wouldn't have learned as many tactics ;) )

Lemma has_const_hasConst´:
  forall e,
  has_const e ->
  hasConst e = true.
Proof.
  intros.
  induction H; simpl; auto.
  + rewrite orb_true_iff. auto.
  + rewrite orb_true_iff. auto.
  + rewrite orb_true_iff. auto.
  + rewrite orb_true_iff. auto.
  + rewrite orb_true_iff. auto.
  + rewrite orb_true_iff. auto.
Qed.

or even better
Lemma has_const_hasConst´´:
  forall e,
  has_const e ->
  hasConst e = true.
Proof.
  intros.
  induction H; simpl; auto;
    rewrite orb_true_iff; auto.
Qed.

Lemma not_has_const_hasConst´´:
  forall e,
  ~ has_const e ->
  hasConst e = false.
Proof.
  unfold not; intros.
  destruct (hasConst e) eqn:?.
  - exfalso. apply H.
    apply hasConst_has_const; auto.
  - reflexivity.
Qed.

Lemma false_hasConst_hasConst´´:
  forall e,
  hasConst e = false ->
  ~ has_const e.
Proof.
  unfold not; intros.
  destruct (hasConst e) eqn:?.
  - discriminate.
  - rewrite has_const_hasConst in Heqb.
NOTE: we got another subgoal!
    * discriminate.
    * assumption.
Qed.

This page has been generated by coqdoc