Details of the solution

Arthur Charguéraud - May 2006

Introduction

Contents of this document

The key idea of the solution has alreay been presented.

In this document, we will give detailed explainations of the definitions, proofs and tactics used.

Some sections of this document are explained on slides. Those slides cover:

We will write "See the slides" to refer to those illustrations.

Plan of this document

We will follow the structure of the COQ file:

  1. Using and extending COQ's library
  2. Types and operations
  3. Tactics for operations
  4. Tactics for crossing lemmas
  5. Crossing lemmas
  6. Properties of operations
  7. Size of types
  8. Induction on size of types
  9. Unsafe subtyping
  10. Results about unsafe subtyping
  11. Environments and well-formation
  12. Subtyping
  13. Subtyping relates to unsafe subtyping
  14. Results about subtyping

In Appendix:

  1. Line counts
  2. Extending the language with pairs

Notations

In this document:

In the COQ file:

Using and extending COQ's library

Implicit arguments

   Set Implicit Arguments.
   Unset Strict Implicit.

These lines tells that we do not need to give all arguments if some of them can be deduced from the others. We use this to make functions easier to work with. Let's see on an example. We have a proposition:

   Lemma weaken_preserves_sub : forall (X : nat) (T1 T2 : typ), 
     T1 <¤ T2 -> (weaken X T1) <¤ (weaken X T2).

And also we have an hypothesis H of type "U1 <¤ U2", and we would like to give "(weaken Y U1) <¤ (weaken Y U2)" as parameter to another function. Then we shall give to that function the result of lemma weaken_preserves_sub applied to X U1 U2 and U1 <¤ U2. Without implicit arguments, we would write

   weaken_preserves_sub Y U1 U2 H

Turning implicit arguments on, COQ is able to guess when we give H to find out that we wanted T1=U1 and T2=U2. Thus we just need to write:

   weaken_preserves_sub Y H

Arith and Omega

   Require Import Arith. 
   Require Import Omega.

Those two lines let us access tactic "omega", an automatic decision procedure for Presburger arithmetic. We use it to prove some simple equalities or inequalities, or to find a contradiction in a set of such relations.

Tactic naming

   Ltac naming E e expr :=
      set (e := expr) in * |- *; assert (E : e = expr); 
      [ trivial | clearbody e ].

Call "naming E e expr" with E and e fresh identifiers and expr being a term. This tactic introduce the equality "E : e = expr" in the context, and replace all occurences of expr by e, both in the goal and in the premisses. This tactic is used to rewrite hypotheses that are used in inductions or in inversions.

Tactic func_equal

   Ltac func_equal :=
     (match goal with
     | [ |- (?F _ = ?F _ ) ] => apply (f_equal F)
     | [ |- (?F _ _ = ?F _ _) ] => apply (f_equal2 F)
     | _ => idtac
     end); auto.

This tactif expects a goal of the form "f x1 x2 .. xN = f y1 y2 .. yN", where f is either a function or a inductive contructor, and generates subgoals xi = yi for all i. This tactic just a convenient way to call COQ tactics "f_equal", "f_equal2", "f_equal3" and so on.

Tactic absurd_maths

   Ltac absurd_maths := intros; assert False; [ omega | contradiction ].

This tactic proves a goal when there exists a contradiction between some of the hypothesis using basic operators such as '=', '+', 'S', '<', '<=' and '>'. This tactic is useful to discriminate impossible cases when doing comparison of De Bruijn indices.

Tactic change_maths

   Tactic Notation "change_maths" constr(T) "with" constr(U) :=
     replace T with U; [ idtac | omega ].

This tactic let us replace a mathematical expression by another one, with the same value. Example of use:

   change_maths (S (S X + Y)) with (S (0+(1+X+Y)))

Types and operations

See the slides for the definitions.

Why we weaken 1+X times

Weaken X does insert 1+X variable in the environment:

Fixpoint weaken (X : nat) (T : typ) {struct X} : typ :=
  match X with
  | O => insert 0 T
  | S P => insert 0 (weaken P T)
  end.

So why didn't we simply insert X variables in the environment?

Fixpoint weaken (X : nat) (T : typ) {struct X} : typ :=
  match X with
  | O => T
  | S P => insert 0 (weaken P T)
  end.

The reason is that a good number of results that we need (for the crossing lemmas) are of the form:

   forall X > 0,  ... some property about (weaken X)

And it makes life much easier in COQ to have:

   forall X >= 0,  ... some property about (weaken (1+X))

Becauses this translates as "forall (X : nat)" and does not require any extra hypothesis.

Why to weaken all at once at the end

In our definition of update, the type U is carried down to the place we want to use it, going through 1+Y bindings, and then at this point it is weakened by inserting 1+Y variables in the environment.

Fixpoint update (X : nat) (U : typ) (T : typ) {struct T} : typ :=
  | all   T1 T2 => all (update X U T1) (update (S X) U T2) 
  | var   Y  T1 => var Y (if eq_nat_dec X Y then weaken Y U
                                            else update X U T1)

There is an equivalent way of doing this which is to weaken U once each time we pass a binding. It would be:

Fixpoint update (X : nat) (U : typ) (T : typ) {struct T} : typ :=
  | all   T1 T2 => all (update X U T1) (update (S X) (insert 0 U) T2) 
  | var   Y  T1 => var Y (if eq_nat_dec X Y then insert 0 U
                                            else update X U T1)

The reason we prefer the first technique is that it simplifies the proofs. Indeed, we don't need to invoke a crossing lemma each time we pass through a binding, we only need to do this at the end on the variable.

Specialized tactics

Tactic analyse_update

   Ltac analyse_update H :=
     match type of H with ?R = update ?X ?U ?T => 
        destruct T; try discriminate; simpl update in H;
        try (injection H; intros; clear H)
     end. 

This is an elaborated inversion tactic for function update, to capture the fact that update go through types and only modify labels. We give an example:

    Input:
      H : arrow U1 U2 = update X U T

    Output:
      H1 : T = arrow T1 T2
      H2 : U1 = T1
      H3 : U2 = T2

Tactic ind_on_weaken

   Tactic Notation "ind_on_weaken" ident(X) :=
     intro X; induction X as [ | X IHX ]; intros; 
     simpl weaken; auto.

This one is used to prove a property about "weaken X" by induction on X. It simply helps to perform the induction, doing the necessary introductions and simplifications, and solving easy cases.

Tactics for crossing lemmas

This part is rather technical, and we are not going to get into all details, but just provide a little summary and give an example:

Let's look at the proof of the crossing lemma insert_above_insert (the actual paper proof is detailed in the next section, here we just show how the tactics prove the lemma).

   Lemma insert_above_insert : forall (T : typ) (X Y : nat),
     insert (S (X + Y)) (insert X T) = insert X (insert (X + Y) T).

We call ind_for_cross, which solves all cases but the one where T is a variable:

   IHT : forall X Y : nat, insert (S (X + Y)) (insert X T) 
                         = insert X (insert (X + Y) T)
   _______________________________________________________(1/1)
   insert (S (X + Y)) (insert X (var n T)) 
   = insert X (insert (X + Y) (var n T))

Then we call case_insert_update, which splits according to comparison between indices:

   E : X <= n  
   E0 : S (X + Y) <= S n  
   E1 : X + Y <= n  
   E2 : X <= S n
   ________________________________________________(1/3)
   var (S (S n)) (insert (S (X + Y)) (insert X T)) 
   = var (S (S n)) (insert X (insert (X + Y) T))


   E : X <= n  
   E0 : S (X + Y) > S n  
   E1 : X + Y > n  
   ________________________________________________(2/3)
   var (S n) (insert (S (X + Y)) (insert X T)) 
   = var (S n) (insert X (insert (X + Y) T))


   E : X > n 
   E0 : S (X + Y) > n  
   E1 : X + Y > n  
   ________________________________________________(3/3)
   var n (insert (S (X + Y)) (insert X T)) 
   = var n (insert X (insert (X + Y) T))

All these cases are easily solved using the induction hypothese IHT which is available (but not shown here) in the three cases.

The final proof of this lemma is:

   ind_for_cross. case_insert_update; use IHT. 

Now let's see the proof of the crossing lemma insert_above_insert:

   Lemma insert_below_weaken : forall (X Y : nat) (T : typ),
      insert X (weaken (X+Y) T) = weaken (1+X+Y) T. 

We call tactic ind_on_weaken X, which solves the base case and leaves us with:

   IHX : forall (Y : nat) (T : typ),
         insert X (weaken (X + Y) T) = weaken (S (X + Y)) T
   __________________________________________________________(1/1)
   insert (S X) (insert 0 (weaken (X + Y) T))
   = insert 0 (insert 0 (weaken (X + Y) T))

This is a instance of lemma insert_above_insert that we have just proved, modulo the invokation of IHX.

For some reasons we need to explicitely tell COQ that X is equal to (X+0) before we can call "rewrite insert_above_insert", and that's what tactic rewrite_for_0 helps us to do.

The final proof of this lemma is:

   ind_on_weaken X. rewrite_for_0 X insert_above_insert. use IHX. 

Crossing lemmas

See the slides for the proof graph of all the crossing lemmas, and also the illustration of the proof for the first crossing lemma. In this section we detail the proofs. We show the full details of the induction on types only for the first lemma, it goes exactly the same way in all other lemmas that use the "ind_for_cross" tactic. In the proofs, we don't detail what happens for the labels of the variables, since it is always just an application of the induction hypothesis.

insert_on_insert

   Lemma insert_on_insert :
   insert X (insert (X+Y) T) = insert (1+X+Y) (insert X T).

  by induction on T
   - case top: 
         LHS = top
         RHS = top
   - case arrow: 
         LHS = arrow (insert X (insert X+Y) T1) (insert X (insert X+Y) T2)
         RHS = arrow (insert (1+X+Y) (insert X T1)) (insert (1+X+Y) (insert X T2))
         apply each of the two induction hypotheses
   - case all: 
         LHS = all (insert X (insert X+Y) T1) (insert 1+X (insert 1+X+Y) T2)
         RHS = all (insert (1+X+Y) (insert X T1)) (insert (1+1+X+Y) (insert (1+X) T2))
         apply each of the two induction hypotheses, IH2 with X+1 instead of X
   - case var Z:
      - case X <= Z:
         - case X+Y <= Z
             LHS = 1+1+Z   (using X <= 1+Z)
             RHS = 1+1+Z   (using 1+X+Y <= 1+Z)
         - case X+Y > Z 
             LHS = 1+Z  
             RHS = 1+Z     (using 1+Z < 1+X+Y)
      - case X > Z:
         LHS = Z (using Z < X+Y)
         RHS = Z (using Z < 1+X+Y)

insert_below_weaken

   Lemma insert_below_weaken :
   insert X (weaken (X+Y) T) = weaken (1+X+Y) T 

   by induction on X (case 0 is trivial):
       insert (1+X) (weaken (1+1+X+Y) T)           then by def of weaken
     = insert (1+X) (insert 0 (weaken (1+X+Y) T))  then by insert_on_insert
     = insert 0 (insert X (weaken (X+Y) T))        then by IH
     = insert 0 (weaken (1+X+Y) T)                 then by def of weaken
     = weaken (1+1+X+Y) T

insert_above_weaken

   Lemma insert_above_weaken : 
   insert (1+X+Y)) (weaken X T) = weaken X (insert Y T). 

   by induction on X (case 0 is trivial):
      insert (1+1+X+Y) (weaken (1+X) T)         then by def of weaken
    = insert (1+1+X+Y) (insert 0 (weaken X T))  then by insert_on_insert
    = insert 0 (insert (0+1+X+Y) (weaken X T))  then by IH
    = insert 0 (weaken (X (insert Y T))         then by def of weaken
    = weaken (1+X) (insert Y T)

update_at_insert

   Lemma insert_above_weaken : 
   update X U (insert X T) = insert X T.

      by induction on X T:
      - case T = var Z T1:
            update X U (insert X (var Z T1)) = insert X (var Z T1)
         - case X <= Z
            LHS: Z+1    (using X <> Z+1)
            RHS: Z+1
         - case X > Z
            LHS: Z
            RHS: Z

update_above_insert

   Lemma update_above_insert : 
   update (1+X+Y) U (insert X T) = insert X (update (X+Y) U T).

   - case Z = 1+X+Y:
      LHS = weaken (1+X+Y) U          (using 1+Z = 1+X+Y)
      RHS = insert X (weaken (X+Y) U)
      conclude using insert_below_weaken
   - case Z <> X+Y
      - case X <= Z
          LHS = 1+Z  (using 1+Z <> 1+X+Y)
          RHS = 1+Z 
      - case X > Z 
          LHS = Z
          RHS = Z

insert_above_update

   Lemma insert_above_update : 
   insert (1+X+Y) (update X U T) = update X (insert Y U) (insert (1+X+Y) T).

   - case Z = Y:
      LHS = insert (1+X+Y) (weaken Y U)
      RHS = weaken X (insert Y U)        (using 1+X+Y <> X)
      conclude using insert_above_weaken 
   - case Z <> Y
      - case 1+X+Y <= Z
          LHS = 1+Z
          RHS = 1+Z    (using 1+Z <> X)
      - case 1+X+Y > Z 
          LHS = Z
          RHS = Z

update_below_weaken

   Lemma update_below_weaken :
   update X U (weaken (X+Y) T) = weaken (X+Y) T.

     by induction on X:
     - case X = 0
          goal = update 0 U (weaken Y T) = weaken Y T.
          by induction on Y
          - case Y = 0
             goal = update 0 U (insert 0 T) = insert 0 T.
             this is update_at_insert
          - case Y = Y'+1 
             trivial using induction hypothesis
     - case X = X'+1
          update (1+X) U (weaken (1+X+Y) T)           then by def of weaken
        = update (1+X) U (insert 0 (weaken (X+Y) T))  then by update_above_insert 
        = insert 0 (update X U (weaken (X+Y) T))      then by IHX
        = insert 0 (weaken (X+Y) T)                   then by def of weaken
        = weaken (1+X+Y) T.

insert_on_insert

   Lemma update_above_weaken =
   update (1+X+Y) U (weaken X T) = weaken X (update Y U T).

     by induction on X
     - case X = 0
         update (1+Y) U (insert 0 T) = insert 0 (update Y U T).
         this is an instance of update_above_insert
     - case X = X'+1
         update (1+1+X+Y) U (weaken (1+X) T)        then by def of weaken
       = update (1+1+X+Y) U (insert 0 (weaken X T)) then by update_above_insert
       = insert 0 (weaken X (update Y U T))         then by IHX
       = weaken (1+X) (update Y U T).

update_above_update


   Lemma update_above_update :
   update (1+X+Y) P (update X U T) = update X (update Y P U) (update (1+X+Y) P T).

      by induction on T
      - case var Z:
         - case Z = X, Z = 1+X+Y
               contradiction
         - case Z = X, Z <> 1+X+Y
               LHS = update (1+X+Y) P (weaken X U)
               RHS = update X (update Y P U) Z
               RHS = weaken X (update Y P U)
               conclude using update_above_weaken
         - case Z <> X, Z = 1+X+Y
               LHS = weaken (1+X+Y) P
               RHS = update X (update Y P U) (weaken (1+Y+X) P)
               conclude using update_below_weaken
         - case Z <> X, Z <> 1+Y+X
               LHS = Z
               RHS = Z

Properties of operations

See the slides for the intuition.

insert_on_push

Lemma insert_on_push : 
  insert (1+X) (push P T) = push (insert X P) (insert (1+X) T).

       insert (1+X) (push P T)                   then by def of push
     = insert (1+X) (update 0 P T)               then by insert_above_update
     = update 0 (insert X P) (insert (1+X)) T)   then by def of push
     = push (insert X U) (insert (1+X) T)

change_on_push

Lemma update_on_push :
   update (S X) U (push P T) = push (update X U P) (update (S X) U T)
 
      update (S X) U (push P T)                   then by def of push
    = update (S X) U (update 0 P T)               then by update_above_update
    = update 0 (update X U P) (update (S X) U T)  then by def of push
    = push (update X U P) (update (S X) U T)

update_at_weaken

Lemma update_at_weaken : 
   update X U (weaken X T) = weaken X T.

   this is an instance of update_below_weaken

update_and_equality

Lemma update_and_equality : 
  update X P T1 = update X P T2 -> update X Q T1 = update X Q T2.

   by induction on T1 with a case analysis on T2 using the analyse_update tactic

Induction on size of types

The point of writing these tactics is to get a clear distinction between the arguments used in the induction on the size and the induction on the abstract notion of type. We will use these tactics to prove both reflexivity and transitivity of subtyping.

Introducing size of types

This tactic takes a goal of the form "forall (T : typ), P T", where P is some property over types. It proves it by induction on the size of T. The new goal states that property P is true for all T of size less than s, this statement holding for all size s:

   forall (s : nat) (T : typ), size T < s -> P T

We then invoke an induction on the natural s, and solve the case s = 0 by contradiction. It leaves:

   IHs : forall T : typ, size T < s -> P T
   T : typ
   Hs : size T < S s
   ______________________________________
      P T

To see this tactic in action on sub_reflexivity, you may use the debug mode, or simply expand the tactic:

   Lemma sub_reflexivity' : forall (T : typ), T <¤ T.

     intro T. generalize (lt_n_Sn (size T)).
     naming E s (S (size T)). generalize T. clear E T.
     induction s as [ | s IHs T Hs ]. intros. absurd_by_omega.

     induction T; fix_ind_on_size_hypotheses IHs Hs; auto.

Fixing induction hypotheses

To prove the goal stated above, we would then have an induction on T. This generates one subgoal for each constructor of the inductive structure "typ". We detail the case of arrow to illustrate the working of the tactic that "fixes" induction hypotheses. Here is the goal:

   T1 : typ
   T2 : typ
   IHT1 : size T1 < S s -> P T1
   IHT2 : size T2 < S s -> P T2
   Hs : size (arrow T1 T2) < S s 
   ______________________________________
      P (arrow T1 T2)

The induction hypotheses are not very nice looking. We would like to have the "standard" induction hypotheses which are simply:

   IH1 : P T1
   IH2 : P T2
   ______________________________________
      P (arrow T1 T2)

But this is not quite enough. To go through operations that preserve size, we'll need two other versions, more general:

   IH1': forall T1' : typ, size T1' = size T1 -> P T1'
   IH2': forall T2' : typ, size T2' = size T2 -> P T2'
   ______________________________________
      P (arrow T1 T2)

Our tactic is going to remove hypotheses IHT1, IHT2 and Hs from the context and replace them by IH1 and IH2. To do this, it uses the omega tactic to prove that Hs implies "size T1 < S s", and then it applies it to IHT1 and IHT2, and obtains exactly IH1 and IH2.

Let's look at what happens for the case of abstraction $X<¤T1.T2 (the one we did all this work for). We transform the goal:

   T1 : typ
   T2 : typ
   IHT1 : size T1 < S s -> P T1
   IHT2 : size T2 < S s -> P T2
   Hs : size (all T1 T2) < S s 
   ______________________________________
      P (all T1 T2)

By this one:

   IH1 : P T1
   IH2 : forall U : typ, P (push U T2)
   IH1': forall T1' : typ, size T1' = size T1 -> P T1'
   IH2': forall T2' : typ, size T2' = size T2 -> P T2'
   ______________________________________
     P (all T1 T2)

IH2 states that the property P holds on T2 whatever label is being attached to all instances of X in T2. It is more general than the induction hypothesis "P (push T1 T2)", and we need this flexibility to prove sub_substitution and sub_transitivity.

Unsafe subtyping

See the presentation of the solution.

Results about unsafe subtyping

See the slides for the proof graph.

insertion preserves unsafe subtyping

Lemma insert_preserves_sub : forall (X : nat) (T1 T2 : typ),
  T1 <¤ T2 -> (insert X T1) <¤ (insert X T2).

   we prove that:
      forall (X : nat), (insert X S) <¤ (insert X T)
   by induction on the derivation of S <¤ T 
   we need to use insert_on_push to go through the binding in case SA_all

weakening preserves unsafe subtyping

Lemma weaken_preserves_sub : forall (X : nat) (T1 T2 : typ), 
  T1 <¤ T2 -> (weaken X T1) <¤ (weaken X T2).

   by induction on X, using insert_preserves_sub for both cases

narrowing preserves unsafe subtyping

Lemma narrow_preserves_sub : (assuming transitivity holds)
     [X := Q]S <¤ [X := Q]T -> P <¤ Q -> 
     [X := P]S <¤ [X := P]T 

Proof by induction on the first unsafe subtyping derivation, with induction hypothesis:

   forall X S T,
        U = [X := Q]S ->  
        V = [X := Q]T -> 
        [X := P]S <¤ [X := P]T 

Transitivity of unsafe subtyping

Lemma sub_transitivity : forall (Q S T : typ),
   S <¤ Q -> Q <¤ T -> S <¤ T

We prove this with an induction on the size of Q. In this induction, we have an induction on the first subtyping judgment, and a nested inversion on the second subtyping judgment.

All cases are solved by auto, but the case SA-All that we shall detail here.

- we have the following relations:

      S = $X<¤S1.S2 
      Q = $X<¤Q1.Q2 
      T = $X<¤T1.T2 
    
      Q1 <¤ S1
      (push Q1 S2) <¤ (push Q1 Q2)

      T1 <¤ Q1
      (push T1 Q2) <¤ (push T1 T2)

- IH1 looks like:
      S' <¤ Q1  ->  Q1 <¤ T'  ->  S' <¤ T'

  IH2 looks like:
      S' <¤ (push Q1 Q2)  ->  (push Q1 Q2) <¤ T'  ->  S' <¤ T'

  IH2' is a generalization of IH2 and it looks like:
      size R = size Q2  ->  S' <¤ R  ->  R <¤ T'  ->  S' <¤ T'


- using IH1 with S'=T1 and T'=S1 gives 
      T1 <¤ S1
   
- on hypothesis (push Q1 S2) <¤ (push Q1 Q2) 
  apply update_preserves_sub with X = 0 and T1 <¤ Q1 to get:
      (push T1 S2) <¤ (push T1 Q2)

- now apply IH2' with R = push T1 Q2 (noticing that push preserves size)
  to prove the transitivity on the relations:
      (push T1 S2) <¤ (push T1 Q2) <¤ (push T1 T2)
  
- we prove the goal applying SA-All to the two hypothesis that we have proven:
      T1 <¤ S1
      (push T1 S2) <¤ (push T1 T2)

Proving narrowing and transitivity together

First we define what transitivity and narrowing are:

   Definition sub_trans_prop (Q : typ) := 
     forall (S T : typ), S <¤ Q -> Q <¤ T -> S <¤ T.

   Definition narrow_preserves_sub_prop (Q : typ) := 
     forall (X : nat) (P S T : typ), P <¤ Q -> 
     (update X Q S) <¤ (update X Q T) -> (update X P S) <¤ (update X P T).

The we define the following statement: a proposition G holds for all possible weakening of a type T. Here it is:

   Definition true_for_weaken_of T G := forall X, G (weaken X T).

First we prove narrowing assuming transitivity holds for all weakening of its argument:

   Lemma narrow_preserves_sub_when_trans : forall (Q : typ), 
     true_for_weaken_of Q sub_trans_prop -> narrow_preserves_sub_prop Q.

Then we prove transitivity, using the result about narrowing:

   Lemma sub_transitivity : forall (Q : typ), sub_trans_prop Q.

Finally, we could prove narrowing without the transitivity hypothesis:

   Lemma narrow_preserves_sub : forall Q, narrow_preserves_sub_prop Q.
      intro Q. apply (narrow_preserves_sub_when_trans (Q := Q)).
      unfold true_for_weaken_of. intro X. apply sub_transitivity.
      Qed.

However we don't bother doing it, since narrowing is only used for transitivity.

Environments and well-formation

See the presentation of the solution, and also the slides.

Subtyping

See the presentation of the solution.

Subtyping equivalent to unsafe subtyping

See the presentation of the solution.

The first implication is easy: we extract induction hypotheses given for the root, and then have an induction to prove the unsafe relation. In the induction, we only need to forget about well-formation statements, so "auto" solves it easily.

   Lemma subtyping_to_sub : forall (E : env) (S T : typ), 
      E |- S <: T -> wf_env E /\ wf E S /\ wf E T /\ S <¤ T.
   intros. repeat split; induction H; auto.
   Qed.

The other way needs just a little more work. We need to carry the well-formation hypotheses as we explore the unsafe subtyping relation and want to reconstruct the safe subtyping relation. The difficulty is to desconstruct well-formation hypotheses at each step: this is what the two inversions are for.

   Lemma sub_to_subtyping : forall (S T : typ), S <¤ T -> 
      forall (E : env), wf_env E -> wf E S -> wf E T -> E |- S <: T.
   intros S T H. induction H; intros E wfE wfS wfT;
   inversion wfS; auto; inversion wfT; auto.
   Qed.

Results about subtyping

See the presentation of the solution.

--------------------------------------------------------

Appendix

Line counts

Note: those figures as accurate to 2 or 3 units.

   71 lines of proof                   (among which 20 are for narrowing)
   80 lines of tactics
   57 lines of lemmas statements
   28 lines of functions definitions
   50 lines of inductive definitions
   12 lines of meta controls
   26 lines containg just "Qed"        (i.e. the end of the proof of a lemma)
 ------ 
  329 lines in total                   (without blank lines and comments)

Adding pairs to the solution

I haven't tried to handle part 1B and add records, but I did add pairs. It took 5 minutes, and here is the list of changes:

More precisely, here is the (beautified) diff:

+   | pair  : typ -> typ -> typ.
+   | pair  T1 T2 => pair (insert X T1) (insert X T2)
+   | pair  T1 T2 => pair (update X U T1) (update X U T2)
-   induction T as [ | T1 IHT1 T2 IHT2 | T1 IHT1 T2 IHT2 | ]; intros X Y; 
+   induction T as [ | T1 IHT1 T2 IHT2 | T1 IHT1 T2 IHT2 | | ]; intros X Y; 
+   | simpl; rewrite (IHT1 X Y); rewrite (IHT2 X Y); reflexivity ]).
+   | pair  T1 T2 => 1 + (size T1) + (size T2)
+   | pair ?T1 ?T2 => fix_ih T1; fix_ih T2
+   | SA_prod : forall (T1 T2 U1 U2 : typ), 
+       T1 <¤ U1 -> T2 <¤ U2 -> (pair T1 T2) <¤ (pair U1 U2)
- induction H as [ | | | Y T1 | Y R V RsubV IH X ]; intros.
+ induction H as [ | | | Y T1 | Y R V RsubV IH X | ]; intros.
+ (* case SA_Prod *)
+ analyse_update DU. analyse_update DV. simpl. auto.
+   | wf_prod : forall (E : env) (T1 T2 : typ), 
+        wf E T1 -> wf E T2 -> wf E (pair T1 T2).
+   | SA_Prod : forall (E : env) (T1 T2 U1 U2 : typ), 
+       wf_env E -> wf E (pair T1 T2) -> wf E (pair U1 U2) ->
+       E |- T1 <: U1 -> E |- T2 <: U2 ->   
+       E |- (pair T1 T2) <: (pair U1 U2)