(* Polymorphism and higher-order functions
Version of 1/28/2009
*)
Require Export Listssol.
(* Note that we've changed the Require Export line to
refer directly to our solutions; you should be able to
use your own solutions, so long as you rename them
appropriately. *)
(* ---------------------------------------------------------------------- *)
(* Polymorphic lists *)
(* Lists of booleans *)
Inductive boollist : Set :=
| bool_nil : boollist
| bool_cons : bool -> boollist -> boollist.
(* A datatype for lists with elements drawn from any set X *)
Inductive list (X:Set) : Set :=
| nil : list X
| cons : X -> list X -> list X.
Check nil.
Check cons.
Check (cons nat 2 (cons nat 1 (nil nat))).
(* Polymorphic versions of some functions we've already seen *)
Fixpoint length (X:Set) (l:list X) {struct l} : nat :=
match l with
| nil => 0
| cons h t => S (length X t)
end.
Check length.
Example test_length1 :
length nat (cons nat 1 (cons nat 2 (nil nat))) = 2.
Proof. reflexivity. Qed.
Example test_length2 :
length bool (cons bool true (nil bool)) = 1.
Proof. reflexivity. Qed.
Fixpoint app (X : Set) (l1 l2 : list X) {struct l1}
: (list X) :=
match l1 with
| nil => l2
| cons h t => cons X h (app X t l2)
end.
Fixpoint snoc (X:Set) (l:list X) (v:X) {struct l} : (list X) :=
match l with
| nil => cons X v (nil X)
| cons h t => cons X h (snoc X t v)
end.
Fixpoint rev (X:Set) (l:list X) {struct l} : list X :=
match l with
| nil => nil X
| cons h t => snoc X (rev X t) h
end.
(* Some examples with lists of different types. Note that
we are using [nil] and [cons] because we haven't yet
defined the notations [] or ::. *)
Example test_rev1 :
rev nat (cons nat 1 (cons nat 2 (nil nat)))
= (cons nat 2 (cons nat 1 (nil nat))).
Proof. reflexivity. Qed.
Example test_rev2:
rev bool (nil bool) = nil bool.
Proof. reflexivity. Qed.
(* -------------------------------------------------- *)
(* Argument synthesis *)
(* Supplying every type argument is boring... *)
Definition l123 :=
cons nat 1 (cons nat 2 (cons nat 3 (nil nat))).
(* ... but Coq can usually infer them -- just replace the
type argument by _ and it will use unification to try
to find a reasonable value. *)
Definition l123' := cons _ 1 (cons _ 2 (cons _ 3 (nil _))).
Fixpoint length' (X:Set) (l:list X) {struct l} : nat :=
match l with
| nil => 0
| cons h t => S (length' _ t)
end.
(* -------------------------------------------------- *)
(* Implicit arguments *)
(* To avoid writing too many _'s, we can tell Coq that we
want it ALWAYS to infer the type argument(s) of a given
function. *)
Implicit Arguments nil [X].
Implicit Arguments cons [X].
Implicit Arguments length [X].
Implicit Arguments app [X].
Implicit Arguments rev [X].
Implicit Arguments snoc [X].
(* A small problem: this definition fails, because Coq
doesn't know what implicit argument to use for [nil]...
Definition mynil := nil.
*)
(* To tell Coq that we want to supply a type argument just
this once, even though we've declared the function to
take this argument implicitly, use @ *)
Definition mynil := @nil nat.
(* Now we can define convenient notation for lists, as
before *)
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).
Definition l123'' := [1, 2, 3].
(* -------------------------------------------------- *)
(* Polymorphism exercises *)
(* A few easy exercises, just like ones in Lists.v (for
practice with polymorphism)... *)
(** <<
Fixpoint repeat (X : Set) (n : X) (count : nat) {struct count} : list X :=
FILL IN HERE
Example test_repeat1:
repeat bool true 2 = cons true (cons true nil).
Proof. reflexivity. Qed.
>> *)
Theorem nil_app : forall X:Set, forall l:list X,
app [] l = l.
Proof.
(* FILL IN HERE (and delete "Admitted") *) Admitted.
(* Some more easy exercises, not too different from things
we've seen in Lists.v... *)
Theorem rev_snoc : forall X : Set,
forall v : X,
forall s : list X,
rev (snoc s v) = v :: (rev s).
Proof.
(* FILL IN HERE (and delete "Admitted") *) Admitted.
Theorem snoc_with_append : forall X : Set,
forall l1 l2 : list X,
forall v : X,
snoc (l1 ++ l2) v = l1 ++ (snoc l2 v).
Proof.
(* OPTIONAL EXERCISE *) Admitted.
(* -------------------------------------------------- *)
(* Higher-order functions *)
(* A function that applies another function three times *)
Definition doit3times (X:Set) (f:X->X) (n:X) : X :=
f (f (f n)).
Example test_doit3times1: doit3times nat minustwo 9 = 3.
Proof. reflexivity. Qed.
Example test_doit3times2: doit3times bool negb true = false.
Proof. reflexivity. Qed.
Check doit3times.
Implicit Arguments doit3times [X].
(* -------------------------------------------------- *)
(* Partial application and anonymous functions *)
Check plus.
Definition plus3 := plus 3.
Check plus3.
Example test_plus3 : plus3 4 = 7.
Proof. reflexivity. Qed.
Example test_plus3' : doit3times plus3 0 = 9.
Proof. reflexivity. Qed.
Example test_plus3'' : doit3times (plus 3) 0 = 9.
Proof. reflexivity. Qed.
(* Functions can be defined anonymously, in-line... *)
Example test_anon_fun:
doit3times (fun (n:nat) => mult n n) 2 = 256.
Proof. reflexivity. Qed.
(* Coq can infer the type of the anonymous function here
from the fact that it is being used in a context where
it is applied to a number *)
Example test_anon_fun':
doit3times (fun n => mult n n) 2 = 256.
Proof. reflexivity. Qed.
(* A slightly more complicated anonymous function *)
Example test_doit3times4:
doit3times (fun n => minus (mult n 2) 1) 2 = 9.
Proof. reflexivity. Qed.
(* ---------------------------------------------------------------------- *)
(* Optional exercise: currying. *)
(* In Coq, a function f : A -> B -> C really has the type
A -> (B -> C). That is, if you give f a value of type
A, it will give you function f' : B -> C. If you then
give f' a value of type B, it will return a value of
type C. This allows for partial application, as in
plus3. Processing a list of arguments with functions
that return functions is called "currying", named in
honor of the logician Haskell Curry.
Conversely, we can reinterpret the type A -> B -> C
as (A * B) -> C. This is called "uncurrying". In an
uncurried binary function, both arguments must be given
at once as a pair; there is no partial application. *)
Definition prod_curry (X Y Z : Set)
(f : X * Y -> Z) (x : X) (y : Y) : Z := f (x, y).
(** <<
Definition prod_uncurry (X Y Z : Set)
(f : X -> Y -> Z) (p : X * Y) : Z :=
FILL IN HERE (OPTIONALLY)
Implicit Arguments prod_curry [X Y Z].
Implicit Arguments prod_uncurry [X Y Z].
(* Thought exercise: before running these commands, what
are the types of prod_curry and prod_uncurry? *)
Check prod_curry.
Check prod_uncurry.
Theorem uncurry_curry : forall (X Y Z : Set) (f : X -> Y -> Z) x y,
prod_curry (prod_uncurry f) x y = f x y.
Proof.
(* OPTIONAL EXERCISE *) Admitted.
Theorem curry_uncurry : forall (X Y Z : Set) (f : (X * Y) -> Z) (p : X * Y),
prod_uncurry (prod_curry f) p = f p.
Proof.
(* OPTIONAL EXERCISE *) Admitted.
>> *)
(* -------------------------------------------------- *)
(* Map *)
(* Do something to each element of a list, returning a new
list (possibly of a new type!) *)
Fixpoint map (X:Set) (Y:Set) (f:X->Y) (l:list X) {struct l}
: (list Y) :=
match l with
| [] => []
| h :: t => (f h) :: (map _ _ f t)
end.
Example test_map1: map nat nat (plus 3) [2,0,2] = [5,3,5].
Proof. reflexivity. Qed.
Implicit Arguments map [X Y].
Example test_map2: map oddb [2,1,2,5] = [false,true,false,true].
Proof. reflexivity. Qed.
Example test_map3:
map (fun n => [evenb n,oddb n]) [2,1,2,5] =
[[true,false],[false,true],[true,false],[false,true]].
Proof. reflexivity. Qed.
(* Exercise: show that map and rev commute. You may need
to define an auxiliary lemma. *)
Theorem map_rev : forall (X Y : Set) (f : X -> Y) (l : list X),
map f (rev l) = rev (map f l).
Proof.
(* OPTIONAL EXERCISE *) Admitted.
(* Exercise *)
(** <<
Fixpoint flat_map (X:Set) (Y:Set) (f:X -> list Y) (l:list X) {struct l}
: (list Y) :=
FILL IN HERE
Implicit Arguments flat_map [X Y].
Example test_flat_map1:
flat_map (fun n => [n,n,n]) [1,5,4]
= [1, 1, 1, 5, 5, 5, 4, 4, 4].
Proof. reflexivity. Qed.
>> *)
(* -------------------------------------------------- *)
(* Filter *)
Fixpoint filter (X:Set) (test: X->bool) (l:list X)
{struct l} : (list X) :=
match l with
| [] => []
| h :: t => if test h then h :: (filter _ test t)
else filter _ test t
end.
Implicit Arguments filter [X].
Example test_filter1: filter evenb [1,2,3,4] = [2,4].
Proof. reflexivity. Qed.
Example test_filter2:
filter (fun l => beq_nat (length l) 1)
[ [1, 2], [3], [4], [5,6,7], [], [8] ]
= [ [3], [4], [8] ].
Proof. reflexivity. Qed.
Definition countoddmembers' (l:list nat) : nat :=
length (filter oddb l).
Example test_countoddmembers'1: countoddmembers' [1,0,3,1,4,5] = 4.
Proof. reflexivity. Qed.
Example test_countoddmembers'2: countoddmembers' [0,2,4] = 0.
Proof. reflexivity. Qed.
Example test_countoddmembers'3: countoddmembers' nil = 0.
Proof. reflexivity. Qed.
(* ---------------------------------------------------------------------- *)
(* Polymorphic options *)
Inductive option (X:Set) : Set :=
| Some : X -> option X
| None : option X.
Implicit Arguments Some [X].
Implicit Arguments None [X].
(* A mapping function for options: do something to
(Some x), but leave None alone... *)
Definition map_option (X Y : Set) (f : X -> Y) (xo : option X)
: option Y :=
match xo with
| None => None
| Some x => Some (f x)
end.
Implicit Arguments map_option [X Y].
(* Exercise *)
(** <<
Fixpoint index
FILL IN HERE
Implicit Arguments index [X].
Example test_index1 : index 0 [4,5,6,7] = Some 4.
Proof. reflexivity. Qed.
Example test_index2 : index 1 [[1],[2]] = Some [2].
Proof. reflexivity. Qed.
Example test_index3 : index 2 [true] = None.
Proof. reflexivity. Qed.
>> *)
(* Exercise *)
(** <<
Definition hd_opt (X : Set) (l : list X) : option X :=
FILL IN HERE
Implicit Arguments hd_opt [X].
Example test_hd_opt1 : @hd_opt nat [] = None.
Proof. reflexivity. Qed.
Example test_hd_opt2 : hd_opt [1,2] = Some 1.
Proof. reflexivity. Qed.
Example test_hd_opt3 : hd_opt [[1],[2]] = Some [1].
Proof. reflexivity. Qed.
>> *)
(* ------------------------------------------------------- *)
(* The [unfold] tactic *)
(* The [simpl] tactic doesn't unfold Definitions. The
[unfold] tactic can be used to explicitly replace a
defined name by the right-hand side of its
definition. *)
Eval simpl in (plus 3 5).
Eval simpl in (plus3 5).
(* Doing [simpl] instead of [unfold] here does nothing.
We are stuck unless we know something about the way
plus3 is defined. *)
Theorem unfold_example :
plus3 5 = 8.
Proof.
unfold plus3.
reflexivity.
Qed.
(* ----------------------------------------------------- *)
(* Functions as Data *)
Definition constfun (X : Set) (x : X) :=
fun (k:nat) => x.
Implicit Arguments constfun [X].
Definition override (X : Set) (f : nat->X) (k:nat) (x:X) :=
fun k' => if beq_nat k k' then x else f k'.
Implicit Arguments override [X].
Theorem override_eq : forall (X:Set) x k (f : nat->X),
(override f k x) k = x.
Proof.
intros X x k f.
unfold override.
rewrite <- beq_nat_refl.
reflexivity.
Qed.
(* Before starting to play with tactics, make sure you
understand exactly what this theorem is saying. *)
Theorem override_example : forall (b:bool),
(override (constfun b) 3 true) 2 = b.
Proof.
(* FILL IN HERE (and delete "Admitted") *) Admitted.
Theorem override_neq : forall (X:Set) x1 x2 k1 k2 (f : nat->X),
f k1 = x1 ->
beq_nat k2 k1 = false ->
(override f k2 x2) k1 = x1.
Proof.
(* FILL IN HERE (and delete "Admitted") *) Admitted.
(* ---------------------------------------------------- *)
(* Inversion *)
(* A fundamental property of inductive definitions is that
their constructors are INJECTIVE. For example, the only
way we can have [S n = S m] is if [m = n].
The [inversion] tactic uses this injectivity principle to
derive useful consequences of an equality hypothesis. *)
Theorem eq_add_S : forall (n m : nat),
S n = S m
-> n = m.
Proof.
intros n m eq. inversion eq. reflexivity.
Qed.
(* The same principle applies to other inductively defined
sets, such as lists. *)
Theorem silly4 : forall (n m : nat),
[n] = [m]
-> n = m.
Proof.
intros n o eq. inversion eq. reflexivity.
Qed.
(* As a convenience, the [inversion] tactic can also
destruct equalities between complex values, binding
multiple variables as it goes. *)
Theorem silly5 : forall (n m o : nat),
[n,m] = [o,o]
-> [n] = [m].
Proof.
intros n m o eq. inversion eq. reflexivity.
Qed.
Example sillyex1 : forall (X : Set) (x y z : X) (l j : list X),
x :: y :: l = z :: j
-> y :: l = x :: j
-> x = y.
Proof.
(* FILL IN HERE (and delete "Admitted") *) Admitted.
(* Another critical property of inductive definitions is
that applications of distinct constructors are never
equal, no matter what they are applied to. For
example, [S n] can never be equal to [O], no matter
what [n] is. This means that anytime we can see a
HYPOTHESIS of the form [S n = O], we know that we must
have made contradictory assumptions at some point. The
[inversion] tactic can be applied to this hypothesis to
finish the proof. *)
Theorem silly6 : forall (n : nat),
S n = O
-> plus 2 2 = 5.
Proof.
intros n contra. inversion contra.
Qed.
(* Similarly, if we assume that [false = true], then
anything at all becomes provable. *)
Theorem silly7 : forall (n m : nat),
false = true
-> [n] = [m].
Proof.
intros n m contra. inversion contra.
Qed.
Example sillyex2 : forall (X : Set) (x y z : X) (l j : list X),
x :: y :: l = []
-> y :: l = z :: j
-> x = z.
Proof.
(* FILL IN HERE (and delete "Admitted") *) Admitted.
(* Here is a more realistic use of inversion to prove a
property that we will find useful in many places later
on... *)
Theorem beq_nat_eq : forall n m,
true = beq_nat n m -> n = m.
Proof.
intros n. induction n as [| n'].
Case "n = 0".
intros m. destruct m as [| m'].
SCase "m = 0". reflexivity.
SCase "m = S m'". simpl. intros contra. inversion contra.
Case "n = S n'".
intros m. destruct m as [| m'].
SCase "m = 0". intros contra. inversion contra.
SCase "m = S m'". simpl. intros H.
assert (n' = m') as H1.
apply IHn'. apply H.
rewrite -> H1. reflexivity.
Qed.
(* Informal proof:
Theorem: beq_nat equal numbers are equal.
Proof:
FILL IN HERE
*)
Theorem override_same : forall (X:Set) x1 k1 k2 (f : nat->X),
f k1 = x1 ->
(override f k1 x1) k2 = f k2.
Proof.
(* FILL IN HERE (and delete "Admitted") *) Admitted.
(* Exercise: The above lemma can also be proved by induction
on [m] (though we have to be a little careful about which
order we introduce the variables, so that we get a
general enough induction hypothesis; this is done for you
below). Finish the following proof. To get maximum
benefit from the exercise, try first to do it without
looking back at the one above. *)
Theorem beq_nat_eq' : forall m n,
beq_nat n m = true -> n = m.
Proof.
intros m. induction m as [| m'].
(* FILL IN HERE (and delete "Admitted") *) Admitted.
(* Another illustration of [inversion]. This is a
slightly roundabout way of stating a fact that we have
already proved above. The extra equalities force us to
do a little more equational reasoning and exercise some
of the tactics we've seen recently. *)
Theorem length_snoc' : forall (X : Set) (v : X)
(l : list X) (n : nat),
length l = n
-> length (snoc l v) = S n.
Proof.
intros X v l. induction l as [| v' l'].
Case "l = []". intros n eq. rewrite <- eq. reflexivity.
Case "l = v' :: l'". intros n eq. simpl. destruct n as [| n'].
SCase "n = 0". inversion eq.
SCase "n = S n'".
assert (length (snoc l' v) = S n').
SSCase "Proof of assertion". apply IHl'.
inversion eq. reflexivity.
rewrite -> H. reflexivity.
Qed.
(* Micro-sermon: Mindless proof-hacking is a terrible
temptation in Coq... Try to resist it!! *)
(* -------------------------------------------------- *)
(** ** Practice session *)
(* Some nontrivial but not-too-complicated proofs to work
together in class, and some for you to work as
exercises.
Note that some of the exercises may
involve applying lemmas from earlier lectures or
homeworks. *)
(* A slightly different way of making the same assertion
as above. *)
Theorem length_snoc'' : forall (X : Set) (v : X)
(l : list X) (n : nat),
S (length l) = n
-> length (snoc l v) = n.
Proof.
intros X v l. induction l as [| v' l'].
Case "l = []". intros n eq. rewrite <- eq. reflexivity.
Case "l = v' :: l'".
intros n eq. simpl.
(* Note the care we take here: first we introduce n
and the equality, then we simplify. This leaves
the equation about length UN-simplified, which
means our context will make a little bit more
sense. (The proof will work either way.)
*)
destruct n as [| n'].
(* Now we destruct n; if it's 0, we have a
contradiction immediately. *)
SCase "n = 0". inversion eq.
SCase "n = S n'".
assert (length (snoc l' v) = n').
SSCase "Proof of assertion".
(* We use the IH and the inversion of eq to
prove the equation we want about length. *)
apply IHl'. inversion eq. reflexivity.
rewrite -> H. reflexivity.
Qed.
Theorem beq_nat_0_l : forall n,
true = beq_nat 0 n -> 0 = n.
Proof.
(* FILL IN HERE (and delete "Admitted") *) Admitted.
Theorem beq_nat_0_r : forall n,
true = beq_nat n 0 -> 0 = n.
Proof.
(* FILL IN HERE (and delete "Admitted") *) Admitted.
Fixpoint double (n:nat) {struct n} :=
match n with
| O => O
| S n' => S (S (double n'))
end.
Theorem double_injective : forall n m,
double n = double m
-> n = m.
Proof.
intros n. induction n as [| n'].
(* FILL IN HERE (and delete "Admitted") *) Admitted.
(* --------------------------------------------------------- *)
(* Using tactics on hypotheses *)
(* By default, most tactics work on the goal formula and
leave the context unchanged. But tactics often come
with a variant that performs a similar operation on a
statement in the context. *)
(* For example, the tactic [simpl in H] performs
simplification in the hypothesis named [H] in the
context. *)
Theorem S_inj : forall (n m : nat) (b : bool),
beq_nat (S n) (S m) = b
-> beq_nat n m = b.
Proof.
intros n m b H. simpl in H. apply H.
Qed.
(* Similarly, the tactic [apply L in H] matches some
conditional statement [L] (of the form [L1->L2], say)
against a hypothesis H in the context. However, unlike
ordinary [apply] (which rewrites a goal matching [L2]
into a subgoal [L1]), [apply L in H] matches [H] against
[L1] and, if successful, replaces it with [L2].
In other words, [apply L in H] gives us a form of FORWARD
REASONING -- from [L1->L2] and a hypothesis matching
[L1], it gives us a hypothesis matching [L2].
By contrast, [apply L] is BACKWARD REASONING -- it says
that if we know [L1->L2] and we are trying to prove [L2],
it suffices to prove [L1]. *)
(* Brief digression to record a convenient fact about equality... *)
Theorem sym_eq : forall (X : Set) (n m : X),
n = m -> m = n.
Proof.
intros X n m H. rewrite -> H. reflexivity.
Qed.
(* Here is a variant of a proof from above, using forward
reasoning throughout instead of backward reasoning. *)
Theorem silly3' : forall (n : nat),
(beq_nat n 5 = true -> beq_nat (S (S n)) 7 = true)
-> true = beq_nat n 5
-> true = beq_nat (S (S n)) 7.
Proof.
intros n eq H.
apply sym_eq in H. apply eq in H. apply sym_eq in H.
apply H.
Qed.
Theorem plus_n_n_injective : forall n m,
plus n n = plus m m
-> n = m.
Proof.
intros n. induction n as [| n'].
(* Hint: use the plus_n_Sm lemma *)
(* FILL IN HERE (and delete "Admitted") *) Admitted.
(* ---------------------------------------------------------------------- *)
(* Polymorphic pairs *)
Inductive prod (X Y : Set) : Set :=
pair : X -> Y -> prod X Y.
Implicit Arguments pair [X Y].
Notation "( x , y )" := (pair x y).
Notation "X * Y" := (prod X Y) : type_scope.
Definition fst (X Y : Set) (p : X * Y) : X :=
match p with (x,y) => x end.
Definition snd (X Y : Set) (p : X * Y) : Y :=
match p with (x,y) => y end.
Implicit Arguments snd [X Y].
Implicit Arguments fst [X Y].
Fixpoint combine (X Y : Set) (lx : list X) (ly : list Y)
{struct lx} : list (X*Y) :=
match lx with
| [] => []
| x::tx => match ly with
| [] => []
| y::ty => (x,y) :: (combine _ _ tx ty)
end
end.
Implicit Arguments combine [X Y].
(** <<
(* Define [split] so that it passes the test below *)
Fixpoint split
FILL IN HERE
Implicit Arguments split [X Y].
Example test_split:
split [(1,false),(2,false)] = ([1,2],[false,false]).
Proof. reflexivity. Qed.
>> *)
(* ---------------------------------------------------- *)
(* Using [destruct] on compound expressions *)
(* We have seen many examples where the [destruct] tactic
is used to perform case analysis of the value of some
variable. But sometimes we need to reason by cases on
the result of some EXPRESSION. We can also do this
with [destruct]. *)
Definition sillyfun (n : nat) : bool :=
if beq_nat n 3 then false
else if beq_nat n 5 then false
else false.
Theorem sillyfun_false : forall (n : nat),
sillyfun n = false.
Proof.
intros n. unfold sillyfun.
destruct (beq_nat n 3).
Case "beq_nat n 3 = true". reflexivity.
Case "beq_nat n 3 = false". destruct (beq_nat n 5).
SCase "beq_nat n 5 = true". reflexivity.
SCase "beq_nat n 5 = false". reflexivity.
Qed.
Theorem override_shadow : forall (X:Set) x1 x2 k1 k2 (f : nat->X),
(override (override f k1 x2) k1 x1) k2 = (override f k1 x1) k2.
Proof.
(* FILL IN HERE (and delete "Admitted") *) Admitted.
(** <<
Theorem split_combine : forall X Y (l : list (X * Y)) l1 l2,
split l = (l1, l2) ->
combine l1 l2 = l.
Proof.
intros X Y l. induction l as [| [x y] l'].
(* FILL IN HERE (and delete "Admitted") *) Admitted.
>> *)
(* Thought exercise: We have just proven that for all
lists of pairs, combine is the inverse of split. How
would you state the theorem showing that split is the
inverse of combine?
Hint: what property do you need of l1 and l2 for split
(combine l1 l2) = (l1,l2) to be true?
*)
(* ----------------------------------------------------- *)
(** ** The [remember] tactic *)
(* We have seen how the [destruct] tactic can be used to
perform case analysis of the results of arbitrary
computations. If [e] is an expression whose type is some
inductively defined set [T], then, for each constructor
[c] of [T], [destruct e] generates a subgoal in which all
occurrences of [e] (in the goal and in the context) are
replaced by [c].
Sometimes, however, this substitution process loses
information that we need in order to complete the proof.
For example, suppose we define a function [sillyfun1]
like this... *)
Definition sillyfun1 (n : nat) : bool :=
if beq_nat n 3 then true
else if beq_nat n 5 then true
else false.
(* ... and suppose that we want to convince Coq of the
rather obvious observation that [sillyfun1 n] yields
[true] only when [n] is odd. By analogy with the proofs
we did with [sillyfun] above, it is natural to start the
proof like this: *)
Theorem sillyfun1_odd_FAILED : forall (n : nat),
sillyfun1 n = true
-> oddb n = true.
Proof.
intros n eq. unfold sillyfun1 in eq.
destruct (beq_nat n 3).
Admitted.
(* At this point, we are stuck: the context does not
contain enough information to prove the goal! The
problem is that the substitution peformed by [destruct]
is too brutal -- it threw away every occurrence of
[beq_nat n 3], but we need to keep at least one of these
because we need to be able to reason that since, in
this branch of the case analysis, [beq_nat n 3 = true],
it must be that [n = 3], from which it follows that [n]
is odd. *)
(* What we would really like is not to use [destruct]
directly on [beq_nat n 3] and substitute away all
occurrences of this expression, but rather to use
[destruct] on something else that is EQUAL to [beq_nat n
3] -- e.g., if we had a variable that we knew was equal
to [beq_nat n 3], we could [destruct] this variable
instead.
The [remember] tactic allows us to introduce such a
variable. *)
Theorem sillyfun1_odd : forall (n : nat),
sillyfun1 n = true
-> oddb n = true.
Proof.
intros n eq. unfold sillyfun1 in eq.
remember (beq_nat n 3) as e3.
(* At this point, the context has been enriched with a new
variable [e3] and an assumption that [e3 = beq_nat n 3].
Now if we do [destruct e3]... *)
destruct e3.
(* ... the variable [e3] gets substituted away (it
disappears completely) and we are left with the same
state as at the point where we got stuck above, except
that the context still contains the extra equality
assumption -- now with [true] substituted for [e3] --
which is exactly what we need to make progress. *)
Case "e3 = true". apply beq_nat_eq in Heqe3.
rewrite -> Heqe3. reflexivity.
Case "e3 = false".
(* When we come to the second equality test in the
body of the function we are reasoning about, we can
use [remember] again in the same way, allowing us
to finish the proof. *)
remember (beq_nat n 5) as e5. destruct e5.
SCase "e5 = true".
apply beq_nat_eq in Heqe5.
rewrite -> Heqe5. reflexivity.
SCase "e5 = false". inversion eq.
Qed.
(** Now you try it... *)
(* This one is a bit challenging. Be sure your initial intros go
only up through the parameter on which you want to do induction! *)
Theorem filter_exercise : forall (X : Set) (test : X -> bool)
(x : X) (l lf : list X),
filter test l = x :: lf
-> test x = true.
Proof.
(* OPTIONAL EXERCISE *) Admitted.
(* ----------------------------------------------------- *)
(** ** The [apply ... with ...] tactic *)
(* The following (silly) example uses two rewrites in a row
to get from [ [m,n] ] to [ [r,s] ]. *)
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.
rewrite -> eq1. rewrite -> eq2. reflexivity.
Qed.
(* Since this is a common pattern, we might abstract it out
as a lemma recording once and for all the fact that
equality is transitive. *)
Theorem trans_eq : forall (X:Set) (n m o : X),
n = m -> m = o -> n = o.
Proof.
intros X n m o eq1 eq2. rewrite -> eq1. rewrite -> eq2.
reflexivity.
Qed.
(* Now, we should be able to use [trans_eq] to prove the
above example. However, to do this we need a slight
refinement of the [apply] tactic... *)
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.
(* If we simply tell Coq [apply trans_eq] at this point,
it can tell (by matching the goal against the
conclusion of the lemma) that it should instantiate [X]
with [nat], [n] with [[a,b]], and [o] with [[e,f]].
However, the matching process doesn't determine an
instantiation for [m]: we have to supply one explicitly
by adding [with (m:=[c,d])] to the invocation of
[apply]. *)
apply trans_eq with (m:=[c,d]). apply eq1. apply eq2.
Qed.
(* Actually, we usually don't have to include the name [m]
in the [with] clause; Coq is often smart enough to
figure out which instantiation we're giving. We could
instead write: apply trans_eq with [c,d]. *)
Example trans_eq_exercise : forall (n m o p : nat),
m = (minustwo o)
-> (plus n p) = m
-> (plus n p) = (minustwo o).
Proof.
(* FILL IN HERE (and delete "Admitted") *) Admitted.
Theorem beq_nat_trans : forall n m p,
true = beq_nat n m ->
true = beq_nat m p ->
true = beq_nat n p.
Proof.
(* FILL IN HERE (and delete "Admitted") *) Admitted.
Theorem override_permute : forall (X:Set) x1 x2 k1 k2 k3 (f : nat->X),
false = beq_nat k2 k1 ->
(override (override f k2 x2) k1 x1) k3 = (override (override f k1 x1) k2 x2) k3.
Proof.
(* FILL IN HERE (and delete "Admitted") *) Admitted.
(* ---------------------------------------------------------------------- *)
(* Challenge problem. (This is a tiny bit more difficult
and open-ended, but it will be worth only one point
when the homework is graded.)
Define two recursive Fixpoints, forallb and existsb.
forallb checks whether every element in a list
satisfies a given predicate:
forallb oddb [1,3,5,7,9] = true
forallb negb [false,false] = true
forallb evenb [0,2,4,5] = false
forallb (beq_nat 5) [] = true
existsb checks whether there exists an element in the
list that satisfies a given predicate:
existsb (beq_nat 5) [0,2,3,6] = false
existsb (andb true) [true,true,false] = true
existsb oddb [1,0,0,0,0,3] = true
existsb evenb [] = false
Next, create a NONRECURSIVE Definition, existsb', using
forallb and negb.
Prove that existsb' and existsb have the same behavior.
*)
(** <<
FILL IN HERE
>> *)