Restating lambdas in Coq without extensional function equality? - coq

I am going over Software Foundations. There are two definitions of list reverse function given.
Fixpoint rev (l:natlist) : natlist :=
match l with
| nil => nil
| h :: t => rev t ++ [h]
end.
and a tail-recursive one:
Fixpoint rev_append {X} (l1 l2 : list X) : list X :=
match l1 with
| [] => l2
| x :: l1' => rev_append l1' (x :: l2)
end.
Definition tr_rev {X} (l : list X) : list X :=
rev_append l [].
Here is where the problem arrives. I am asked to prove their equality, with the following theorem stated: Lemma tr_rev_correct : ∀X, #tr_rev X = #rev X.
This generates the following proof state:
1 subgoal
______________________________________(1/1)
forall X : Type, tr_rev = rev
However, even if I do unfold tr_rev (and / or the other two definitions), I end up with something along the lines of:
1 subgoal
______________________________________(1/1)
forall X : Type, (fun l : list X => rev_append l [ ]) = rev
But I can't do anything with this formulation (other than intro X).
What I would like to have is this:
Lemma tr_rev_correct : forall (X : Type) (l : list X), tr_rev l = rev l.
Is there a way to replace the former with the latter without involving functional extensionality? (If I didn't want to restate the lemma that is given by the book.)

Related

Coq - How to proof False when hypotesis is wrong

I made an environment to try to proof what I want/need
I have a posfijo function that says if a list (l1) contains another list (l2) at the end.
So if I add an element to the first list and I use the result as the second list, like l2 = x :: l1, I want to proof that is not possible.
I did this...
Variable G:Set.
Inductive posfijo : list _ -> list _ -> Prop :=
| posfijoB : forall l: list _, posfijo l l
| posfijoI : forall (l1 l2: list _) (a : G), posfijo l1 l2 -> posfijo l1 (cons a l2).
Infix "<<" := (posfijo) (at level 70, right associativity).
Lemma Pref4_a : forall (X:Set)(l: list G)(x:G), ~ (cons x l << l).
Proof.
intros X l x H.
So then my goal is
You should proceed with induction l.

Teach coq to check termination

Coq, unlike many others, accepts an optional explicit parameter,which can be used to indicate the decreasing structure of a fixpoint definition.
From Gallina specification, 1.3.4,
Fixpoint ident params {struct ident0 } : type0 := term0
defines the syntax. but from it, we've known that it must be an identifier, instead of a general measure.
However, in general, there are recursive functions, that the termination is not quite obvious,or it in fact is, but just difficult for the termination checker to find a decreasing structure. For example, following program interleaves two lists,
Fixpoint interleave (A : Set) (l1 l2 : list A) : list A :=
match l1 with
| [] => []
| h :: t => h :: interleave l2 t
end
This function clearly terminates, while Coq just couldn't figure it out. The reason is neither l1 nor l2 are decreasing every cycle. But what if we consider a measure, defined to be length l1 + length l2? Then this measure clearly decreases every recursion.
So my question is, in the case of sophisticated situation, where code is not straightforward to be organized in a termination checkable way, how do you educate coq and convince it to accept the fixpoint definition?
You have multiple options and all of them boil down to structural recursion in the end.
Preamble
From Coq Require Import List.
Import ListNotations.
Set Implicit Arguments.
Structural recursion
Sometimes you can reformulate your algorithm in a structurally recursive way:
Fixpoint interleave1 {A} (l1 l2 : list A) {struct l1} : list A :=
match l1, l2 with
| [], _ => l2
| _, [] => l1
| h1 :: t1, h2 :: t2 => h1 :: h2 :: interleave1 t1 t2
end.
Incidentally, in some cases you can use a trick with nested fixes -- see this definition of Ackermann function (it wouldn't work with just Fixpoint).
Program Fixpoint
You can use Program Fixpoint mechanism which lets you write your program naturally and later prove that it always terminates.
From Coq Require Import Program Arith.
Program Fixpoint interleave2 {A} (l1 l2 : list A)
{measure (length l1 + length l2)} : list A :=
match l1 with
| [] => l2
| h :: t => h :: interleave2 l2 t
end.
Next Obligation. simpl; rewrite Nat.add_comm; trivial with arith. Qed.
Function
Another option is to use the Function command which can be somewhat limited compared to Program Fixpoint. You can find out more about their differences here.
From Coq Require Recdef.
Definition sum_len {A} (ls : (list A * list A)) : nat :=
length (fst ls) + length (snd ls).
Function interleave3 {A} (ls : (list A * list A))
{measure sum_len ls} : list A :=
match ls with
| ([], _) => []
| (h :: t, l2) => h :: interleave3 (l2, t)
end.
Proof.
intros A ls l1 l2 h t -> ->; unfold sum_len; simpl; rewrite Nat.add_comm; trivial with arith.
Defined.
Equations plugin
This is an external plugin which addresses many issues with defining functions in Coq, including dependent types and termination.
From Equations Require Import Equations.
Equations interleave4 {A} (l1 l2 : list A) : list A :=
interleave4 l1 l2 by rec (length l1 + length l2) lt :=
interleave4 nil l2 := l2;
interleave4 (cons h t) l2 := cons h (interleave4 l2 t).
Next Obligation. rewrite Nat.add_comm; trivial with arith. Qed.
The code above works if you apply this fix.
Fix / Fix_F_2 combinators
You can learn more about this (manual) approach if you follow the links from this question about mergeSort function. By the way, the mergeSort function can be defined without using Fix if you apply the nested fix trick I mentioned earlier. Here is a solution which uses Fix_F_2 combinator since we have two arguments and not one like mergeSort:
Definition ordering {A} (l1 l2 : list A * list A) : Prop :=
length (fst l1) + length (snd l1) < length (fst l2) + length (snd l2).
Lemma ordering_wf' {A} : forall (m : nat) (p : list A * list A),
length (fst p) + length (snd p) <= m -> Acc (#ordering A) p.
Proof.
unfold ordering; induction m; intros p H; constructor; intros p'.
- apply Nat.le_0_r, Nat.eq_add_0 in H as [-> ->].
intros contra%Nat.nlt_0_r; contradiction.
- intros H'; eapply IHm, Nat.lt_succ_r, Nat.lt_le_trans; eauto.
Defined.
Lemma ordering_wf {A} : well_founded (#ordering A).
Proof. now red; intro ; eapply ordering_wf'. Defined.
(* it's in the stdlib but unfortunately opaque -- this blocks evaluation *)
Lemma destruct_list {A} (l : list A) :
{ x:A & {tl:list A | l = x::tl} } + { l = [] }.
Proof.
induction l as [|h tl]; [right | left]; trivial.
exists h, tl; reflexivity.
Defined.
Definition interleave5 {A} (xs ys : list A) : list A.
refine (Fix_F_2 (fun _ _ => list A)
(fun (l1 l2 : list A)
(interleave : (forall l1' l2', ordering (l1', l2') (l1, l2) -> list A)) =>
match destruct_list l1 with
| inright _ => l2
| inleft pf => let '(existT _ h (exist _ tl eq)) := pf in
h :: interleave l2 tl _
end) (ordering_wf (xs,ys))).
Proof. unfold ordering; rewrite eq, Nat.add_comm; auto.
Defined.
Evaluation tests
Check eq_refl : interleave1 [1;2;3] [4;5;6] = [1;4;2;5;3;6].
Check eq_refl : interleave2 [1;2;3] [4;5;6] = [1;4;2;5;3;6].
Check eq_refl : interleave3 ([1;2;3], [4;5;6]) = [1;4;2;5;3;6].
Fail Check eq_refl : interleave4 [1;2;3] [4;5;6] = [1;4;2;5;3;6]. (* Equations plugin *)
Check eq_refl : interleave5 [1;2;3] [4;5;6] = [1;4;2;5;3;6].
Exercise: what happens with this last check if you comment out destruct_list lemma?
You can use something called a measure instead of a structural argument for termination. For this, I believe you have to use the Program Fixpoint mechanism, which is a little involved and will make your proofs look uglier (because it generates a structural recursion out of the proof that you provide, so that the function you will actually use is not quite the function you wrote).
Details here:
https://coq.inria.fr/refman/program.html
It also seems like something called Equations can deal with measures?
cf. http://mattam82.github.io/Coq-Equations/examples/RoseTree.html
https://www.irif.fr/~sozeau/research/coq/equations.en.html

Using 'unfold' of a Fixpoint inside the recursive step of the induction

I'm trying to prove something in coq and the same problem keeps coming up;
I want to unfold the definition of a Fixpoint inside the recursive (not nil) step of induction. Unfold works as expected, here's an example:
Before unfolding the list reverse (rev) definition:
n : nat
l' : natlist
IHl' : rev (rev l') = l'
============================
rev (rev (n :: l')) = n :: l'
After:
n : nat
l' : natlist
IHl' : rev (rev l') = l'
============================
(fix rev (l : natlist) : natlist := match l with
| [ ] => [ ]
| h :: t => rev t ++ [h]
end)
((fix rev (l : natlist) : natlist := match l with
| [ ] => [ ]
| h :: t => rev t ++ [h]
end) l' ++ [n]) = n :: l'
So far so good. Now I would expect simpl to figure out I'm on the non-nil case of the induction since n :: l' can never be nil,
and simplify away the nil case of the match ([ ] => [ ]), keeping only the non-nil part of the definition.
Unfortunately it does not do that implicitly. How can I make unfold of a recursive Fixpoint definition play well with induction? How do I get:
n : nat
l' : natlist
IHl' : rev (rev l') = l'
============================
rev (rev l' ++ [n]) = n :: l'
According to the rev definition for the inner rev.
Note: The use of Lists is irrelevant here, the same technique can be used for any inductively defined types.
Edit: Definition of rev and proof that leads to the After state.
Fixpoint rev (l:natlist) : natlist :=
match l with
| nil => nil
| h :: t => rev t ++ [h]
end.
Theorem rev_involutive : forall l : natlist,
rev (rev l) = l.
Proof.
intros l. induction l as [| n l'].
- reflexivity.
- unfold rev.
Your After: is basically rev (rev l' ++ [n]) (with rev unfolded) which means that the reduction you want to see happening has already happened. Now you probably want to prove an auxiliary lemma akin to rev (xs ++ ys) = rev ys ++ rev xs.

Coq rewriting using lambda arguments

We have a function that inserts an element into a specific index of a list.
Fixpoint inject_into {A} (x : A) (l : list A) (n : nat) : option (list A) :=
match n, l with
| 0, _ => Some (x :: l)
| S k, [] => None
| S k, h :: t => let kwa := inject_into x t k
in match kwa with
| None => None
| Some l' => Some (h :: l')
end
end.
The following property of the aforementioned function is of relevance to the problem (proof omitted, straightforward induction on l with n not being fixed):
Theorem inject_correct_index : forall A x (l : list A) n,
n <= length l -> exists l', inject_into x l n = Some l'.
And we have a computational definition of permutations, with iota k being a list of nats [0...k]:
Fixpoint permute {A} (l : list A) : list (list A) :=
match l with
| [] => [[]]
| h :: t => flat_map (
fun x => map (
fun y => match inject_into h x y with
| None => []
| Some permutations => permutations
end
) (iota (length t))) (permute t)
end.
The theorem we're trying to prove:
Theorem num_permutations : forall A (l : list A) k,
length l = k -> length (permute l) = factorial k.
By induction on l we can (eventually) get to following goal: length (permute (a :: l)) = S (length l) * length (permute l). If we now simply cbn, the resulting goal is stated as follows:
length
(flat_map
(fun x : list A =>
map
(fun y : nat =>
match inject_into a x y with
| Some permutations => permutations
| None => []
end) (iota (length l))) (permute l)) =
length (permute l) + length l * length (permute l)
Here I would like to proceed by destruct (inject_into a x y), which is impossible considering x and y are lambda arguments. Please note that we will never get the None branch as a result of the lemma inject_correct_index.
How does one proceed from this proof state? (Please do note that I am not trying to simply complete the proof of the theorem, that's completely irrelevant.)
There is a way to rewrite under binders: the setoid_rewrite tactic (see §27.3.1 of the Coq Reference manual).
However, direct rewriting under lambdas is not possible without assuming an axiom as powerful as the axiom of functional extensionality (functional_extensionality).
Otherwise, we could have proved:
(* classical example *)
Goal (fun n => n + 0) = (fun n => n).
Fail setoid_rewrite <- plus_n_O.
Abort.
See here for more detail.
Nevertheless, if you are willing to accept such axiom, then you can use the approach described by Matthieu Sozeau in this Coq Club post to rewrite under lambdas like so:
Require Import Coq.Logic.FunctionalExtensionality.
Require Import Coq.Setoids.Setoid.
Require Import Coq.Classes.Morphisms.
Generalizable All Variables.
Instance pointwise_eq_ext {A B : Type} `(sb : subrelation B RB eq)
: subrelation (pointwise_relation A RB) eq.
Proof. intros f g Hfg. apply functional_extensionality. intro x; apply sb, (Hfg x). Qed.
Goal (fun n => n + 0) = (fun n => n).
setoid_rewrite <- plus_n_O.
reflexivity.
Qed.

Subsets of list nat in coq

I defined a recursive function for all subsets of nat_list in coq as
Fixpoint subsets (a: list nat) : (list (list nat)) :=
match a with
|[] => [[]]
|h::t => subsets t ++ map (app [h]) (subsets t)
end.
I am trying to prove that
forall (a:list nat), In [] (subsets a).
I tried to induct on a. The base-case was straight forward. However in the induction case i tried to use the in-built theorem in_app_or.
Unable to unify "In ?M1396 ?M1394 \/ In ?M1396 ?M1395" with
"(fix In (a : list nat) (l : list (list nat)) {struct l} : Prop :=
match l with
| [] => False
| b :: m => b = a \/ In a m
end)
[] (subsets t ++ map (fun m : list nat => h :: m) (subsets t))".
How do I prove such a theorem or get around such an issue?
The problem with in_app_or is that is has the following type:
forall (A : Type) (l m : list A) (a : A),
In a (l ++ m) -> In a l \/ In a m
and application of lemmas to the goal works "backwards": Coq matches the consequent B of the implication A -> B with the goal, and if they can be unified, you are left with a new goal: you need to prove a (stronger) statement A. And in your case the A and B are in the wrong order (swapped), so you need to apply in_or_app instead:
in_or_app : forall (A : Type) (l m : list A) (a : A),
In a l \/ In a m -> In a (l ++ m)
This is how your goal can be proved using in_or_app:
Goal forall (a:list nat), In [] (subsets a).
intros.
induction a; simpl; auto.
apply in_or_app; auto.
Qed.