10.3 Examples

10.3.1 About the cardinality of the natural number set

A first example which shows how to use the pattern matching over the proof contexts is the proof that natural numbers have more than two elements. The proof of such a lemma can be done as shown in table 10.4.


6in
Coq < Lemma card_nat: ~(EX x:nat|(EX y:nat|(z:nat)(x=z)/\(y=z))).

Coq < Proof.

Coq < Red;Intro H.

Coq < Elim H;Intros a Ha.

Coq < Elim Ha;Intros b Hb.

Coq < Elim (Hb (0));Elim (Hb (1));Elim (Hb (2));Intros;
Coq <   Match Context With
Coq <     [_:?1=?2;_:?1=?3|-?] ->
Coq <       Cut ?2=?3;[Discriminate|Apply trans_equal with ?1;Auto].

Coq < Save.

Table 10.4: A proof on cardinality of natural numbers


We can notice that all the (very similar) cases coming from the three eliminations (with three distinct natural numbers) are successfully solved by a Match Context structure and, in particular, with only one pattern (use of non-linear unification).

10.3.2 Permutation on closed lists

Another more complex example is the problem of permutation on closed lists. The aim is to show that a closed list is a permutation of another one.

First, we define the permutation predicate as shown in table 10.5.


6in
Coq < Section Sort.

Coq < Variable A:Set.

Coq < 
Coq < Inductive permut:(list A)->(list A)->Prop:=
Coq <   permut_refl:(l:(list A))(permut l l)
Coq <  |permut_cons:
Coq <     (a:A)(l0,l1:(list A))(permut l0 l1)->
Coq <       (permut (cons a l0) (cons a l1))
Coq <  |permut_append:
Coq <     (a:A)(l:(list A))(permut (cons a l) (l^(cons a (nil A))))
Coq <  |permut_trans:
Coq <     (l0,l1,l2:(list A))(permut l0 l1)->(permut l1 l2)->
Coq <       (permut l0 l2).

Coq < End Sort.

Table 10.5: Definition of the permutation predicate


Next, we can write naturally the tactic and the result can be seen in table 10.6. We can notice that we use two toplevel definitions PermutProve and Permut. The function to be called is PermutProve which computes the lengths of the two lists and calls Permut with the length if the two lists have the same length. Permut works as expected. If the two lists are equal, it concludes. Otherwise, if the lists have identical first elements, it applies Permut on the tail of the lists. Finally, if the lists have different first elements, it puts the first element of one of the lists (here the second one which appears in the permut predicate) at the end if that is possible, i.e., if the new first element has been at this place previously. To verify that all rotations have been done for a list, we use the length of the list as an argument for Permut and this length is decremented for each rotation down to, but not including, 1 because for a list of length n, we can make exactly n-1 rotations to generate at most n distinct lists. Here, it must be noticed that we use the natural numbers of Coq for the rotation counter. In table 10.1, we can see that it is possible to use usual natural numbers but they are only used as arguments for primitive tactics and they cannot be handled, in particular, we cannot make computations with them. So, a natural choice is to use Coq data structures so that Coq makes the computations (reductions) by Eval Compute in and we can get the terms back by Match.


6in
Coq < Tactic Definition Permut n:=
Coq <  Match Context With
Coq <    [|-(permut ? ?1 ?1)] -> Apply permut_refl
Coq <   |[|-(permut ? (cons ?1 ?2) (cons ?1 ?3))] ->
Coq <      Let newn=Eval Compute in (length ?2) In
Coq <      Apply permut_cons;(Permut newn)
Coq <   |[|-(permut ?1 (cons ?2 ?3) ?4)] ->
Coq <      (Match Eval Compute in n With
Coq <         [(1)] -> Fail
Coq <        |_ ->
Coq <          Let l0'='(?3^(cons ?2 (nil ?1))) In
Coq <          Apply (permut_trans ?1 (cons ?2 ?3) l0' ?4);
Coq <          [Apply permut_append|
Coq <           Compute;(Permut '(pred n))]).
Permut is defined

Coq < 
Coq < Tactic Definition PermutProve:=
Coq <  Match Context With
Coq <    [|-(permut ? ?1 ?2)] ->
Coq <      (Match Eval Compute in ((length ?1)=(length ?2)) With
Coq <         [?1=?1] -> (Permut ?1)).
PermutProve is defined

Table 10.6: Permutation tactic


With PermutProve, we can now prove lemmas such those shown in table 10.7.


6in
Coq < Lemma permut_ex1:
Coq <   (permut nat (cons (1) (cons (2) (cons (3) (nil nat))))
Coq <      (cons (3) (cons (2) (cons (1) (nil nat))))).

Coq < Proof.

Coq < PermutProve.

Coq < Save.

Coq < 
Coq < Lemma permut_ex2:
Coq <  (permut nat
Coq <    (cons (0) (cons (1) (cons (2) (cons (3) (cons (4) (cons (5)
Coq <      (cons (6) (cons (7) (cons (8) (cons (9) (nil nat)))))))))))
Coq <    (cons (0) (cons (2) (cons (4) (cons (6) (cons (8) (cons (9)
Coq <      (cons (7) (cons (5) (cons (3) (cons (1) (nil nat)))))))))))).

Coq < Proof.

Coq < PermutProve.

Coq < Save.

Table 10.7: Examples of PermutProve use


10.3.3 Deciding intuitionistic propositional logic

The pattern matching on proof contexts allows a complete and so a powerful backtracking when returning tactic values. An interesting application is the problem of deciding intuitionistic propositional logic. Considering the contraction-free sequent calculi LJT* of Roy Dyckhoff ([Dyc92]), it is quite natural to code such a tactic using the tactic language as shown in table 10.8. The tactic Axioms tries to conclude using usual axioms. The tactic Simplif applies all the reversible rules of Dyckhoff's system. Finally, the tactic TautoProp (the main tactic to be called) simplifies with Simplif, tries to conclude with Axioms and tries several paths using the backtracking rules (one of the four Dyckhoff's rules for the left implication to get rid of the contraction and the right or).


6in
Coq < Tactic Definition Axioms:=
Coq <   Match Context With
Coq <    [|-True] -> Trivial
Coq <   |[_:False|- ?] -> ElimType False;Assumption
Coq <   |[_:?1|-?1] -> Auto.
Axioms is defined

Coq < 
Coq < Tactic Definition Simplif:=
Coq <  Repeat
Coq <   (Intros;
Coq <    (Match Context With
Coq <      [id:~?|-?] -> Red in id
Coq <     |[id:?/\?|-?] -> Elim id;Do 2 Intro;Clear id
Coq <     |[id:?\/?|-?] -> Elim id;Intro;Clear id
Coq <     |[id:?1/\?2->?3|-?] ->
Coq <        Cut ?1->?2->?3;[Intro|Intros;Apply id;Split;Assumption]
Coq <     |[id:?1\/?2->?3|-?] ->
Coq <        Cut ?2->?3;[Cut ?1->?3;[Intros|
Coq <                                Intro;Apply id;Left;Assumption]|
Coq <                    Intro;Apply id;Right;Assumption]
Coq <     |[id0:?1->?2;id1:?1|-?] ->
Coq <        Cut ?2;[Intro;Clear id0|Apply id0;Assumption]
Coq <     |[|-?/\?] -> Split
Coq <     |[|-~?] -> Red)).
Simplif is defined

Coq < 
Coq < Recursive Tactic Definition TautoProp:=
Coq <   Simplif;
Coq <    Axioms
Coq <    Orelse
Coq <    Match Context With
Coq <     [id:(?1->?2)->?3|-?] ->
Coq <       Cut ?2->?3;[Intro;Cut ?1->?2;[Intro;Cut ?3;[Intro;Clear id|
Coq <                               Apply id;Assumption]|Clear id]|
Coq <                   Intro;Apply id;Intro;Assumption];TautoProp
Coq <    |[id:~?1->?2|-?]->
Coq <       Cut False->?2;
Coq <       [Intro;Cut ?1->False;[Intro;Cut ?2;[Intro;Clear id|
Coq <                                Apply id;Assumption]|Clear id]|
Coq <        Intro;Apply id;Red;Intro;Assumption];TautoProp
Coq <    |[|-?\/?] ->
Coq <       (Left;TautoProp) Orelse (Right;TautoProp).
TautoProp is defined

Table 10.8: Deciding intuitionistic propositions


For example, with TautoProp, we can prove tautologies like those in table 10.9.


6in
Coq < Lemma tauto_ex1:(A,B:Prop)A/\B->A\/B.

Coq < Proof.

Coq < TautoProp.

Coq < Save.

Coq < 
Coq < Lemma tauto_ex2:(A,B:Prop)(~~B->B)->(A->B)->~~A->B.

Coq < Proof.

Coq < TautoProp.

Coq < Save.

Table 10.9: Proofs of tautologies with TautoProp


10.3.4 Deciding type isomorphisms

A more tricky problem is to decide equalities between types and modulo isomorphisms. Here, we choose to use the isomorphisms of the simply typed l-calculus with Cartesian product and unit type (see, for example, [RC95]). The axioms of this l-calculus are given by table 10.10.


6in
Coq < Section Iso_axioms.

Coq < 
Coq < Variable A,B,C:Set.

Coq < 
Coq < Axiom Com:(A*B)==(B*A).

Coq < Axiom Ass:(A*(B*C))==((A*B)*C).

Coq < Axiom Cur:((A*B)->C)==(A->B->C).

Coq < Axiom Dis:(A->(B*C))==((A->B)*(A->C)).

Coq < Axiom P_unit:(A*unit)==A.

Coq < Axiom AR_unit:(A->unit)==unit.

Coq < Axiom AL_unit:(unit->A)==A.

Coq < 
Coq < Lemma Cons:B==C->(A*B)==(A*C).

Coq < Proof.

Coq < Intro Heq;Rewrite Heq;Apply refl_eqT.

Coq < Save.

Coq < 
Coq < End Iso_axioms.

Table 10.10: Type isomorphism axioms


The tactic to judge equalities modulo this axiomatization can be written as shown in tables 10.11 and 10.12. The algorithm is quite simple. Types are reduced using axioms that can be oriented (this done by MainSimplif). The normal forms are sequences of Cartesian products without Cartesian product in the left component. These normal forms are then compared modulo permutation of the components (this is done by CompareStruct). The main tactic to be called and realizing this algorithm is IsoProve.


6in
Coq < Recursive Tactic Definition Simplif trm:=
Coq <   Match trm With
Coq <    [(?1*?2)*?3] -> Rewrite <- (Ass ?1 ?2 ?3);Try MainSimplif
Coq <   |[(?1*?2)->?3] -> Rewrite (Cur ?1 ?2 ?3);Try MainSimplif
Coq <   |[?1->(?2*?3)] -> Rewrite (Dis ?1 ?2 ?3);Try MainSimplif
Coq <   |[?1*unit] -> Rewrite (P_unit ?1);Try MainSimplif
Coq <   |[unit*?1] -> Rewrite (Com unit ?1);Try MainSimplif
Coq <   |[?1->unit] -> Rewrite (AR_unit ?1);Try MainSimplif
Coq <   |[unit-> ?1] -> Rewrite (AL_unit ?1);Try MainSimplif
Coq <   |[?1*?2] ->
Coq <      ((Simplif ?1);Try MainSimplif) Orelse
Coq <      ((Simplif ?2);Try MainSimplif)
Coq <   |[?1-> ?2] ->
Coq <      ((Simplif ?1);Try MainSimplif) Orelse
Coq <      ((Simplif ?2);Try MainSimplif)
Coq < And MainSimplif:=
Coq <   Match Context With
Coq <    [|- ?1== ?2] -> Try (Simplif ?1);Try (Simplif ?2).
MainSimplif is defined
Simplif is defined

Coq < 
Coq < Tactic Definition Length trm:=
Coq <   Match trm With
Coq <    [?*?1] ->
Coq <      Let succ=(Length ?1) In
Coq <      '(S succ)
Coq <   |_ -> '(1).
Length is defined

Coq < 
Coq < Tactic Definition Assoc:= Repeat Rewrite <- Ass.
Assoc is defined

Table 10.11: Type isomorphism tactic (1)



6in
Coq < Recursive Tactic Definition DoCompare n:=
Coq <   Match Context With
Coq <    [|-?1==?1] -> Apply refl_eqT
Coq <   |[|-(?1*?2)==(?1*?3)] ->
Coq <      Apply Cons;
Coq <      Let newn=(Length ?2) In
Coq <      (DoCompare newn)
Coq <   |[|-(?1*?2)==?3] ->
Coq <      (Match Eval Compute in n With
Coq <        [(1)] -> Fail
Coq <       |_ -> 
Coq <         Pattern 1 (?1*?2);Rewrite Com;Assoc;
Coq <         (DoCompare '(pred n))).
DoCompare is defined

Coq < 
Coq < Tactic Definition CompareStruct:=
Coq <   Match Context With
Coq <    [|-?1==?2] ->
Coq <      Let l1=(Length ?1)
Coq <      And l2=(Length ?2) In
Coq <      (Match Eval Compute in l1=l2 With
Coq <         [?1=?1] -> (DoCompare ?1)).
CompareStruct is defined

Coq < 
Coq < Tactic Definition IsoProve:=MainSimplif;CompareStruct.
IsoProve is defined

Table 10.12: Type isomorphism tactic (2)


Table 10.13 gives examples of what can be solved by IsoProve.


6in
Coq < Lemma isos_ex1:(A,B:Set)((A*unit)*B)==(B*(unit*A)).

Coq < Proof.

Coq < Intros;IsoProve.

Coq < Save.

Coq < 
Coq < Lemma isos_ex2:(A,B,C:Set)
Coq <   ((A*unit)->(B*C*unit))==
Coq <   (((A*unit)->((C->unit)*C))*(unit->(A->B))).

Coq < Proof.

Coq < Intros;IsoProve.

Coq < Save.

Table 10.13: Type equalities solved by IsoProve