MoreStlcMore on the Simply Typed Lambda-Calculus
Require Export Stlc.
Simple Extensions to STLC
Numbers
let-bindings
       t ::=                Terms
           | ...               (other terms same as before)
           | let x=t in t      let-binding
| t1 ⇒ t1' | (ST_Let1) | 
| let x=t1 in t2 ⇒ let x=t1' in t2 | 
| (ST_LetValue) | |
| let x=v1 in t2 ⇒ [x:=v1]t2 | 
| Γ ⊢ t1 : T1 Γ , x:T1 ⊢ t2 : T2 | (T_Let) | 
| Γ ⊢ let x=t1 in t2 : T2 | 
Pairs
       λx:Nat*Nat. 
          let sum = x.fst + x.snd in
          let diff = x.fst - x.snd in
          (sum,diff)
       t ::=                Terms
           | ...               
           | (t,t)             pair
           | t.fst             first projection
           | t.snd             second projection
       v ::=                Values
           | ...
           | (v,v)             pair value
       T ::=                Types
           | ...
           | T * T             product type
| t1 ⇒ t1' | (ST_Pair1) | 
| (t1,t2) ⇒ (t1',t2) | 
| t2 ⇒ t2' | (ST_Pair2) | 
| (v1,t2) ⇒ (v1,t2') | 
| t1 ⇒ t1' | (ST_Fst1) | 
| t1.fst ⇒ t1'.fst | 
| (ST_FstPair) | |
| (v1,v2).fst ⇒ v1 | 
| t1 ⇒ t1' | (ST_Snd1) | 
| t1.snd ⇒ t1'.snd | 
| (ST_SndPair) | |
| (v1,v2).snd ⇒ v2 | 
| Γ ⊢ t1 : T1 Γ ⊢ t2 : T2 | (T_Pair) | 
| Γ ⊢ (t1,t2) : T1*T2 | 
| Γ ⊢ t1 : T11*T12 | (T_Fst) | 
| Γ ⊢ t1.fst : T11 | 
| Γ ⊢ t1 : T11*T12 | (T_Snd) | 
| Γ ⊢ t1.snd : T12 | 
Unit
       t ::=                Terms
           | ...               
           | unit              unit value
       v ::=                Values
           | ...     
           | unit              unit
       T ::=                Types
           | ...
           | Unit              Unit type
    Typing:
| (T_Unit) | |
| Γ ⊢ unit : Unit | 
Sums
       Nat + Bool
inl : Nat -> Nat + Bool inr : Bool -> Nat + Bool
    div : Nat -> Nat -> (Nat + Unit) =
    div =
      λx:Nat. λy:Nat.
        if iszero y then
          inr unit
        else
          inl ...
    The type Nat + Unit above is in fact isomorphic to option nat
    in Coq, and we've already seen how to signal errors with options. 
    getNat = 
      λx:Nat+Bool.
        case x of
          inl n => n
        | inr b => if b then 1 else 0
       t ::=                Terms
           | ...               
           | inl T t           tagging (left)
           | inr T t           tagging (right)
           | case t of         case
               inl x => t
             | inr x => t 
       v ::=                Values
           | ...
           | inl T v           tagged value (left)
           | inr T v           tagged value (right)
       T ::=                Types
           | ...
           | T + T             sum type
| t1 ⇒ t1' | (ST_Inl) | 
| inl T t1 ⇒ inl T t1' | 
| t1 ⇒ t1' | (ST_Inr) | 
| inr T t1 ⇒ inr T t1' | 
| t0 ⇒ t0' | (ST_Case) | 
| case t0 of inl x1 ⇒ t1 | inr x2 ⇒ t2 ⇒ | |
| case t0' of inl x1 ⇒ t1 | inr x2 ⇒ t2 | 
| (ST_CaseInl) | |
| case (inl T v0) of inl x1 ⇒ t1 | inr x2 ⇒ t2 | |
| ⇒ [x1:=v0]t1 | 
| (ST_CaseInr) | |
| case (inr T v0) of inl x1 ⇒ t1 | inr x2 ⇒ t2 | |
| ⇒ [x2:=v0]t2 | 
| Γ ⊢ t1 : T1 | (T_Inl) | 
| Γ ⊢ inl T2 t1 : T1 + T2 | 
| Γ ⊢ t1 : T2 | (T_Inr) | 
| Γ ⊢ inr T1 t1 : T1 + T2 | 
| Γ ⊢ t0 : T1+T2 | |
| Γ , x1:T1 ⊢ t1 : T | |
| Γ , x2:T2 ⊢ t2 : T | (T_Case) | 
| Γ ⊢ case t0 of inl x1 ⇒ t1 | inr x2 ⇒ t2 : T | 
Lists
    λx:List Nat.  
    lcase x of nil -> 0 
       | a::x' -> lcase x' of nil -> a
                     | b::x'' -> a+b 
       t ::=                Terms
           | ...
           | nil T
           | cons t t
           | lcase t of nil -> t | x::x -> t
       v ::=                Values
           | ...
           | nil T             nil value
           | cons v v          cons value
       T ::=                Types
           | ...
           | List T            list of Ts
| t1 ⇒ t1' | (ST_Cons1) | 
| cons t1 t2 ⇒ cons t1' t2 | 
| t2 ⇒ t2' | (ST_Cons2) | 
| cons v1 t2 ⇒ cons v1 t2' | 
| t1 ⇒ t1' | (ST_Lcase1) | 
| (lcase t1 of nil → t2 | xh::xt → t3) ⇒ | |
| (lcase t1' of nil → t2 | xh::xt → t3) | 
| (ST_LcaseNil) | |
| (lcase nil T of nil → t2 | xh::xt → t3) | |
| ⇒ t2 | 
| (ST_LcaseCons) | |
| (lcase (cons vh vt) of nil → t2 | xh::xt → t3) | |
| ⇒ [xh:=vh,xt:=vt]t3 | 
| (T_Nil) | |
| Γ ⊢ nil T : List T | 
| Γ ⊢ t1 : T Γ ⊢ t2 : List T | (T_Cons) | 
| Γ ⊢ cons t1 t2: List T | 
| Γ ⊢ t1 : List T1 | |
| Γ ⊢ t2 : T | |
| Γ , h:T1, t:List T1 ⊢ t3 : T | (T_Lcase) | 
| Γ ⊢ (lcase t1 of nil → t2 | h::t → t3) : T | 
General Recursion
   fact = λx:Nat. 
             if x=0 then 1 else x * (fact (pred x)))    
   But this would require quite a bit of work to formalize: we'd have
   to introduce a notion of "function definitions" and carry around an
   "environment" of such definitions in the definition of the step
   relation. 
   fact = 
       fix
         (λf:Nat->Nat.
            λx:Nat. 
               if x=0 then 1 else x * (f (pred x)))    
       t ::=                Terms
           | ...
           | fix t             fixed-point operator
   Reduction:
| t1 ⇒ t1' | (ST_Fix1) | 
| fix t1 ⇒ fix t1' | 
| F = λxf:T1.t2 | (ST_FixAbs) | 
| fix F ⇒ [xf:=fix F]t2 | 
| Γ ⊢ t1 : T1->T1 | (T_Fix) | 
| Γ ⊢ fix t1 : T1 | 
fix F 3⇒ ST_FixAbs
(λx. if x=0 then 1 else x * (fix F (pred x))) 3⇒ ST_AppAbs
if 3=0 then 1 else 3 * (fix F (pred 3))⇒ ST_If0_Nonzero
3 * (fix F (pred 3))⇒ ST_FixAbs + ST_Mult2
3 * ((λx. if x=0 then 1 else x * (fix F (pred x))) (pred 3))⇒ ST_PredNat + ST_Mult2 + ST_App2
3 * ((λx. if x=0 then 1 else x * (fix F (pred x))) 2)⇒ ST_AppAbs + ST_Mult2
3 * (if 2=0 then 1 else 2 * (fix F (pred 2)))⇒ ST_If0_Nonzero + ST_Mult2
3 * (2 * (fix F (pred 2)))⇒ ST_FixAbs + 2 x ST_Mult2
3 * (2 * ((λx. if x=0 then 1 else x * (fix F (pred x))) (pred 2)))⇒ ST_PredNat + 2 x ST_Mult2 + ST_App2
3 * (2 * ((λx. if x=0 then 1 else x * (fix F (pred x))) 1))⇒ ST_AppAbs + 2 x ST_Mult2
3 * (2 * (if 1=0 then 1 else 1 * (fix F (pred 1))))⇒ ST_If0_Nonzero + 2 x ST_Mult2
3 * (2 * (1 * (fix F (pred 1))))⇒ ST_FixAbs + 3 x ST_Mult2
3 * (2 * (1 * ((λx. if x=0 then 1 else x * (fix F (pred x))) (pred 1))))⇒ ST_PredNat + 3 x ST_Mult2 + ST_App2
3 * (2 * (1 * ((λx. if x=0 then 1 else x * (fix F (pred x))) 0)))⇒ ST_AppAbs + 3 x ST_Mult2
3 * (2 * (1 * (if 0=0 then 1 else 0 * (fix F (pred 0)))))⇒ ST_If0Zero + 3 x ST_Mult2
3 * (2 * (1 * 1))⇒ ST_MultNats + 2 x ST_Mult2
3 * (2 * 1)⇒ ST_MultNats + ST_Mult2
3 * 2⇒ ST_MultNats
6
Exercise: 1 star (halve_fix)
Translate this informal recursive definition into one using fix:
   halve = 
     λx:Nat. 
        if x=0 then 0 
        else if (pred x)=0 then 0
        else 1 + (halve (pred (pred x))))
(* FILL IN HERE *)☐
Exercise: 1 star (fact_steps)
Write down the sequence of steps that the term fact 1 goes through to reduce to a normal form (assuming the usual reduction rules for arithmetic operations).☐
    fix (λx:T.x)
 
    By T_Fix  and T_Abs, this term has type T.  By ST_FixAbs
    it reduces to itself, over and over again.  Thus it is an 
    undefined element of T. 
    equal = 
      fix 
        (λeq:Nat->Nat->Bool.
           λm:Nat. λn:Nat.
             if m=0 then iszero n 
             else if n=0 then false
             else eq (pred m) (pred n))
    evenodd = 
      fix 
        (λeo: (Nat->Bool * Nat->Bool).
           let e = λn:Nat. if n=0 then true  else eo.snd (pred n) in
           let o = λn:Nat. if n=0 then false else eo.fst (pred n) in
           (e,o))
    even = evenodd.fst
    odd  = evenodd.snd
Records
       t ::=                          Terms
           | ...
           | {i1=t1, ..., in=tn}         record 
           | t.i                         projection
       v ::=                          Values
           | ...
           | {i1=v1, ..., in=vn}         record value
       T ::=                          Types
           | ...
           | {i1:T1, ..., in:Tn}         record type
   Intuitively, the generalization is pretty obvious.  But it's worth
   noticing that what we've actually written is rather informal: in
   particular, we've written "..." in several places to mean "any
   number of these," and we've omitted explicit mention of the usual
   side-condition that the labels of a record should not contain
   repetitions.  It is possible to devise informal notations that are
   more precise, but these tend to be quite heavy and to obscure the
   main points of the definitions.  So we'll leave these a bit loose
   here (they are informal anyway, after all) and do the work of
   tightening things up elsewhere (in chapter Records). 
| ti ⇒ ti' | (ST_Rcd) | 
| {i1=v1, ..., im=vm, in=ti, ...} | |
| ⇒ {i1=v1, ..., im=vm, in=ti', ...} | 
| t1 ⇒ t1' | (ST_Proj1) | 
| t1.i ⇒ t1'.i | 
| (ST_ProjRcd) | |
| {..., i=vi, ...}.i ⇒ vi | 
| Γ ⊢ t1 : T1 ... Γ ⊢ tn : Tn | (T_Rcd) | 
| Γ ⊢ {i1=t1, ..., in=tn} : {i1:T1, ..., in:Tn} | 
| Γ ⊢ t : {..., i:Ti, ...} | (T_Proj) | 
| Γ ⊢ t.i : Ti | 
Encoding Records (Optional)
-  We can directly formalize the syntactic forms and inference
        rules, staying as close as possible to the form we've given
        them above.  This is conceptually straightforward, and it's
        probably what we'd want to do if we were building a real
        compiler — in particular, it will allow is to print error
        messages in the form that programmers will find easy to
        understand.  But the formal versions of the rules will not be
        pretty at all!
-  We could look for a smoother way of presenting records — for
        example, a binary presentation with one constructor for the
        empty record and another constructor for adding a single field
        to an existing record, instead of a single monolithic
        constructor that builds a whole record at once.  This is the
        right way to go if we are primarily interested in studying the
        metatheory of the calculi with records, since it leads to
        clean and elegant definitions and proofs.  Chapter Records
        shows how this can be done.
- Alternatively, if we like, we can avoid formalizing records altogether, by stipulating that record notations are just informal shorthands for more complex expressions involving pairs and product types. We sketch this approach here.
    {}                 ---->  unit
    {t1, t2, ..., tn}  ---->  (t1, trest)
                              where {t2, ..., tn} ----> trest
    Similarly, we can encode tuple types using nested product types:
    {}                 ---->  Unit
    {T1, T2, ..., Tn}  ---->  T1 * TRest
                              where {T2, ..., Tn} ----> TRest
    The operation of projecting a field from a tuple can be encoded
    using a sequence of second projections followed by a first projection: 
    t.0        ---->  t.fst
    t.(n+1)    ---->  (t.snd).n
      LABEL   POSITION
      a       0
      b       1
      c       2
      ...     ...
      foo     1004
      ...     ...
      bar     10562
      ...     ...
      {a=5, b=6}      ---->   {5,6}
      {a=5, c=7}      ---->   {5,unit,7}
      {c=7, a=5}      ---->   {5,unit,7}
      {c=5, b=3}      ---->   {unit,3,5}
      {f=8,c=5,a=7}   ---->   {7,unit,5,unit,unit,8}
      {f=8,c=5}       ---->   {unit,unit,5,unit,unit,8}
    Note that each field appears in the position associated with its
    label, that the size of the tuple is determined by the label with
    the highest position, and that we fill in unused positions with
    unit.  
      {a:Nat, b:Nat}      ---->   {Nat,Nat}
      {c:Nat, a:Nat}      ---->   {Nat,Unit,Nat}
      {f:Nat,c:Nat}       ---->   {Unit,Unit,Nat,Unit,Unit,Nat}
      t.l  ---->  t.(position of l)
Variants (Optional Reading)
Exercise: Formalizing the Extensions
Exercise: 4 stars, advanced (STLC_extensions)
Formalizing the extensions (not including the optional ones) is left to you. We've provided the necessary extensions to the syntax of terms and types, and we've included a few examples that you can test your definitions with to make sure they are working as expected. You'll fill in the rest of the definitions and extend all the proofs accordingly.- numbers first (since they are both familiar and simple)
- products and units
- let (which involves binding)
- sums (which involve slightly more complex binding)
- lists (which involve yet more complex binding)
- fix
Module STLCExtended.
Inductive ty : Type :=
| TArrow : ty → ty → ty
| TNat : ty
| TUnit : ty
| TProd : ty → ty → ty
| TSum : ty → ty → ty
| TList : ty → ty.
Tactic Notation "T_cases" tactic(first) ident(c) :=
first;
[ Case_aux c "TArrow" | Case_aux c "TNat"
| Case_aux c "TProd" | Case_aux c "TUnit"
| Case_aux c "TSum" | Case_aux c "TList" ].
Inductive tm : Type :=
(* pure STLC *)
| tvar : id → tm
| tapp : tm → tm → tm
| tabs : id → ty → tm → tm
(* numbers *)
| tnat : nat → tm
| tsucc : tm → tm
| tpred : tm → tm
| tmult : tm → tm → tm
| tif0 : tm → tm → tm → tm
(* pairs *)
| tpair : tm → tm → tm
| tfst : tm → tm
| tsnd : tm → tm
(* units *)
| tunit : tm
(* let *)
| tlet : id → tm → tm → tm
(* i.e., let x = t1 in t2 *)
(* sums *)
| tinl : ty → tm → tm
| tinr : ty → tm → tm
| tcase : tm → id → tm → id → tm → tm
(* i.e., case t0 of inl x1 ⇒ t1 | inr x2 ⇒ t2 *)
(* lists *)
| tnil : ty → tm
| tcons : tm → tm → tm
| tlcase : tm → tm → id → id → tm → tm
(* i.e., lcase t1 of | nil → t2 | x::y → t3 *)
(* fix *)
| tfix : tm → tm.
Note that, for brevity, we've omitted booleans and instead
    provided a single if0 form combining a zero test and a
    conditional.  That is, instead of writing
       if x = 0 then ... else ...
    we'll write this:
       if0 x then ... else ...
Tactic Notation "t_cases" tactic(first) ident(c) :=
first;
[ Case_aux c "tvar" | Case_aux c "tapp" | Case_aux c "tabs"
| Case_aux c "tnat" | Case_aux c "tsucc" | Case_aux c "tpred"
| Case_aux c "tmult" | Case_aux c "tif0"
| Case_aux c "tpair" | Case_aux c "tfst" | Case_aux c "tsnd"
| Case_aux c "tunit" | Case_aux c "tlet"
| Case_aux c "tinl" | Case_aux c "tinr" | Case_aux c "tcase"
| Case_aux c "tnil" | Case_aux c "tcons" | Case_aux c "tlcase"
| Case_aux c "tfix" ].
Fixpoint subst (x:id) (s:tm) (t:tm) : tm :=
match t with
| tvar y ⇒
if eq_id_dec x y then s else t
| tabs y T t1 ⇒
tabs y T (if eq_id_dec x y then t1 else (subst x s t1))
| tapp t1 t2 ⇒
tapp (subst x s t1) (subst x s t2)
(* FILL IN HERE *)
| _ ⇒ t (* ... and delete this line *)
end.
Notation "'[' x ':=' s ']' t" := (subst x s t) (at level 20).
Inductive value : tm → Prop :=
| v_abs : ∀x T11 t12,
value (tabs x T11 t12)
(* FILL IN HERE *)
.
Hint Constructors value.
Reserved Notation "t1 '⇒' t2" (at level 40).
Inductive step : tm → tm → Prop :=
| ST_AppAbs : ∀x T11 t12 v2,
value v2 →
(tapp (tabs x T11 t12) v2) ⇒ [x:=v2]t12
| ST_App1 : ∀t1 t1' t2,
t1 ⇒ t1' →
(tapp t1 t2) ⇒ (tapp t1' t2)
| ST_App2 : ∀v1 t2 t2',
value v1 →
t2 ⇒ t2' →
(tapp v1 t2) ⇒ (tapp v1 t2')
(* FILL IN HERE *)
where "t1 '⇒' t2" := (step t1 t2).
Tactic Notation "step_cases" tactic(first) ident(c) :=
first;
[ Case_aux c "ST_AppAbs" | Case_aux c "ST_App1" | Case_aux c "ST_App2"
(* FILL IN HERE *)
].
Notation multistep := (multi step).
Notation "t1 '⇒*' t2" := (multistep t1 t2) (at level 40).
Hint Constructors step.
Definition context := partial_map ty.
Next we define the typing rules.  These are nearly direct
    transcriptions of the inference rules shown above. 
Reserved Notation "Gamma '⊢' t '∈' T" (at level 40).
Inductive has_type : context → tm → ty → Prop :=
(* Typing rules for proper terms *)
| T_Var : ∀Γ x T,
Γ x = Some T →
Γ ⊢ (tvar x) ∈ T
| T_Abs : ∀Γ x T11 T12 t12,
(extend Γ x T11) ⊢ t12 ∈ T12 →
Γ ⊢ (tabs x T11 t12) ∈ (TArrow T11 T12)
| T_App : ∀T1 T2 Γ t1 t2,
Γ ⊢ t1 ∈ (TArrow T1 T2) →
Γ ⊢ t2 ∈ T1 →
Γ ⊢ (tapp t1 t2) ∈ T2
(* FILL IN HERE *)
where "Gamma '⊢' t '∈' T" := (has_type Γ t T).
Hint Constructors has_type.
Tactic Notation "has_type_cases" tactic(first) ident(c) :=
first;
[ Case_aux c "T_Var" | Case_aux c "T_Abs" | Case_aux c "T_App"
(* FILL IN HERE *)
].
Examples
Module Examples.
Notation a := (Id 0).
Notation f := (Id 1).
Notation g := (Id 2).
Notation l := (Id 3).
Notation k := (Id 6).
Notation i1 := (Id 7).
Notation i2 := (Id 8).
Notation x := (Id 9).
Notation y := (Id 10).
Notation processSum := (Id 11).
Notation n := (Id 12).
Notation eq := (Id 13).
Notation m := (Id 14).
Notation evenodd := (Id 15).
Notation even := (Id 16).
Notation odd := (Id 17).
Notation eo := (Id 18).
Next, a bit of Coq hackery to automate searching for typing
    derivations.  You don't need to understand this bit in detail —
    just have a look over it so that you'll know what to look for if
    you ever find yourself needing to make custom extensions to
    auto.
 
    The following Hint declarations say that, whenever auto
    arrives at a goal of the form (Γ ⊢ (tapp e1 e1) ∈ T), it
    should consider eapply T_App, leaving an existential variable
    for the middle type T1, and similar for lcase. That variable
    will then be filled in during the search for type derivations for
    e1 and e2.  We also include a hint to "try harder" when
    solving equality goals; this is useful to automate uses of
    T_Var (which includes an equality as a precondition). 
Hint Extern 2 (has_type _ (tapp _ _) _) ⇒
eapply T_App; auto.
(* You'll want to uncomment the following line once
you've defined the T_Lcase constructor for the typing
relation: *)
(*
Hint Extern 2 (has_type _ (tlcase _ _ _ _ _) _) =>
eapply T_Lcase; auto.
*)
Hint Extern 2 (_ = _) ⇒ compute; reflexivity.
Module Numtest.
(* if0 (pred (succ (pred (2 * 0))) then 5 else 6 *)
Definition test :=
tif0
(tpred
(tsucc
(tpred
(tmult
(tnat 2)
(tnat 0)))))
(tnat 5)
(tnat 6).
Remove the comment braces once you've implemented enough of the
    definitions that you think this should work. 
(*
Example typechecks :
(@empty ty) |- test ∈ TNat.
Proof.
unfold test.
(* This typing derivation is quite deep, so we need to increase the
max search depth of auto from the default 5 to 10. *)
auto 10.
Qed.
Example numtest_reduces :
test ==>* tnat 5.
Proof.
unfold test. normalize.
Qed.
*)
End Numtest.
Module Prodtest.
(* ((5,6),7).fst.snd *)
Definition test :=
tsnd
(tfst
(tpair
(tpair
(tnat 5)
(tnat 6))
(tnat 7))).
(*
Example typechecks :
(@empty ty) |- test ∈ TNat.
Proof. unfold test. eauto 15. Qed.
Example reduces :
test ==>* tnat 6.
Proof. unfold test. normalize. Qed.
*)
End Prodtest.
Module LetTest.
(* let x = pred 6 in succ x *)
Definition test :=
tlet
x
(tpred (tnat 6))
(tsucc (tvar x)).
(*
Example typechecks :
(@empty ty) |- test ∈ TNat.
Proof. unfold test. eauto 15. Qed.
Example reduces :
test ==>* tnat 6.
Proof. unfold test. normalize. Qed.
*)
End LetTest.
Module Sumtest1.
(* case (inl Nat 5) of
inl x => x
| inr y => y *)
Definition test :=
tcase (tinl TNat (tnat 5))
x (tvar x)
y (tvar y).
(*
Example typechecks :
(@empty ty) |- test ∈ TNat.
Proof. unfold test. eauto 15. Qed.
Example reduces :
test ==>* (tnat 5).
Proof. unfold test. normalize. Qed.
*)
End Sumtest1.
Module Sumtest2.
(* let processSum =
λx:Nat+Nat.
case x of
inl n => n
inr n => if0 n then 1 else 0 in
(processSum (inl Nat 5), processSum (inr Nat 5)) *)
Definition test :=
tlet
processSum
(tabs x (TSum TNat TNat)
(tcase (tvar x)
n (tvar n)
n (tif0 (tvar n) (tnat 1) (tnat 0))))
(tpair
(tapp (tvar processSum) (tinl TNat (tnat 5)))
(tapp (tvar processSum) (tinr TNat (tnat 5)))).
(*
Example typechecks :
(@empty ty) |- test ∈ (TProd TNat TNat).
Proof. unfold test. eauto 15. Qed.
Example reduces :
test ==>* (tpair (tnat 5) (tnat 0)).
Proof. unfold test. normalize. Qed.
*)
End Sumtest2.
Module ListTest.
(* let l = cons 5 (cons 6 (nil Nat)) in
lcase l of
nil => 0
| x::y => x*x *)
Definition test :=
tlet l
(tcons (tnat 5) (tcons (tnat 6) (tnil TNat)))
(tlcase (tvar l)
(tnat 0)
x y (tmult (tvar x) (tvar x))).
(*
Example typechecks :
(@empty ty) |- test ∈ TNat.
Proof. unfold test. eauto 20. Qed.
Example reduces :
test ==>* (tnat 25).
Proof. unfold test. normalize. Qed.
*)
End ListTest.
Module FixTest1.
(* fact := fix
(λf:nat->nat.
λa:nat.
if a=0 then 1 else a * (f (pred a))) *)
Definition fact :=
tfix
(tabs f (TArrow TNat TNat)
(tabs a TNat
(tif0
(tvar a)
(tnat 1)
(tmult
(tvar a)
(tapp (tvar f) (tpred (tvar a))))))).
(Warning: you may be able to typecheck fact but still have some
    rules wrong!) 
(*
Example fact_typechecks :
(@empty ty) |- fact ∈ (TArrow TNat TNat).
Proof. unfold fact. auto 10.
Qed.
*)
(*
Example fact_example:
(tapp fact (tnat 4)) ==>* (tnat 24).
Proof. unfold fact. normalize. Qed.
*)
End FixTest1.
Module FixTest2.
(* map :=
λg:nat->nat.
fix
(λf:nat->nat.
λl:nat.
case l of
| ->
| x::l -> (g x)::(f l)) *)
Definition map :=
tabs g (TArrow TNat TNat)
(tfix
(tabs f (TArrow (TList TNat) (TList TNat))
(tabs l (TList TNat)
(tlcase (tvar l)
(tnil TNat)
a l (tcons (tapp (tvar g) (tvar a))
(tapp (tvar f) (tvar l))))))).
(*
(* Make sure you've uncommented the last Hint Extern above... *)
Example map_typechecks :
empty |- map ∈
(TArrow (TArrow TNat TNat)
(TArrow (TList TNat)
(TList TNat))).
Proof. unfold map. auto 10. Qed.
Example map_example :
tapp (tapp map (tabs a TNat (tsucc (tvar a))))
(tcons (tnat 1) (tcons (tnat 2) (tnil TNat)))
==>* (tcons (tnat 2) (tcons (tnat 3) (tnil TNat))).
Proof. unfold map. normalize. Qed.
*)
End FixTest2.
Module FixTest3.
(* equal =
fix
(λeq:Nat->Nat->Bool.
λm:Nat. λn:Nat.
if0 m then (if0 n then 1 else 0)
else if0 n then 0
else eq (pred m) (pred n)) *)
Definition equal :=
tfix
(tabs eq (TArrow TNat (TArrow TNat TNat))
(tabs m TNat
(tabs n TNat
(tif0 (tvar m)
(tif0 (tvar n) (tnat 1) (tnat 0))
(tif0 (tvar n)
(tnat 0)
(tapp (tapp (tvar eq)
(tpred (tvar m)))
(tpred (tvar n)))))))).
(*
Example equal_typechecks :
(@empty ty) |- equal ∈ (TArrow TNat (TArrow TNat TNat)).
Proof. unfold equal. auto 10.
Qed.
*)
(*
Example equal_example1:
(tapp (tapp equal (tnat 4)) (tnat 4)) ==>* (tnat 1).
Proof. unfold equal. normalize. Qed.
*)
(*
Example equal_example2:
(tapp (tapp equal (tnat 4)) (tnat 5)) ==>* (tnat 0).
Proof. unfold equal. normalize. Qed.
*)
End FixTest3.
Module FixTest4.
(* let evenodd =
fix
(λeo: (Nat->Nat * Nat->Nat).
let e = λn:Nat. if0 n then 1 else eo.snd (pred n) in
let o = λn:Nat. if0 n then 0 else eo.fst (pred n) in
(e,o)) in
let even = evenodd.fst in
let odd = evenodd.snd in
(even 3, even 4)
*)
Definition eotest :=
tlet evenodd
(tfix
(tabs eo (TProd (TArrow TNat TNat) (TArrow TNat TNat))
(tpair
(tabs n TNat
(tif0 (tvar n)
(tnat 1)
(tapp (tsnd (tvar eo)) (tpred (tvar n)))))
(tabs n TNat
(tif0 (tvar n)
(tnat 0)
(tapp (tfst (tvar eo)) (tpred (tvar n))))))))
(tlet even (tfst (tvar evenodd))
(tlet odd (tsnd (tvar evenodd))
(tpair
(tapp (tvar even) (tnat 3))
(tapp (tvar even) (tnat 4))))).
(*
Example eotest_typechecks :
(@empty ty) |- eotest ∈ (TProd TNat TNat).
Proof. unfold eotest. eauto 30.
Qed.
*)
(*
Example eotest_example1:
eotest ==>* (tpair (tnat 0) (tnat 1)).
Proof. unfold eotest. normalize. Qed.
*)
End FixTest4.
End Examples.
Properties of Typing
Theorem progress : ∀t T,
empty ⊢ t ∈ T →
value t ∨ ∃t', t ⇒ t'.
Proof with eauto.
(* Theorem: Suppose empty |- t : T. Then either
1. t is a value, or
2. t ==> t' for some t'.
Proof: By induction on the given typing derivation. *)
intros t T Ht.
remember (@empty ty) as Γ.
generalize dependent HeqGamma.
has_type_cases (induction Ht) Case; intros HeqGamma; subst.
Case "T_Var".
(* The final rule in the given typing derivation cannot be T_Var,
since it can never be the case that empty ⊢ x : T (since the
context is empty). *)
inversion H.
Case "T_Abs".
(* If the T_Abs rule was the last used, then t = tabs x T11 t12,
which is a value. *)
left...
Case "T_App".
(* If the last rule applied was T_App, then t = t1 t2, and we know
from the form of the rule that
empty ⊢ t1 : T1 → T2
empty ⊢ t2 : T1
By the induction hypothesis, each of t1 and t2 either is a value
or can take a step. *)
right.
destruct IHHt1; subst...
SCase "t1 is a value".
destruct IHHt2; subst...
SSCase "t2 is a value".
(* If both t1 and t2 are values, then we know that
t1 = tabs x T11 t12, since abstractions are the only values
that can have an arrow type. But
(tabs x T11 t12) t2 ⇒ [x:=t2]t12 by ST_AppAbs. *)
inversion H; subst; try (solve by inversion).
∃(subst x t2 t12)...
SSCase "t2 steps".
(* If t1 is a value and t2 ⇒ t2', then t1 t2 ⇒ t1 t2'
by ST_App2. *)
inversion H0 as [t2' Hstp]. ∃(tapp t1 t2')...
SCase "t1 steps".
(* Finally, If t1 ⇒ t1', then t1 t2 ⇒ t1' t2 by ST_App1. *)
inversion H as [t1' Hstp]. ∃(tapp t1' t2)...
(* FILL IN HERE *)
Qed.
Inductive appears_free_in : id → tm → Prop :=
| afi_var : ∀x,
appears_free_in x (tvar x)
| afi_app1 : ∀x t1 t2,
appears_free_in x t1 → appears_free_in x (tapp t1 t2)
| afi_app2 : ∀x t1 t2,
appears_free_in x t2 → appears_free_in x (tapp t1 t2)
| afi_abs : ∀x y T11 t12,
y ≠ x →
appears_free_in x t12 →
appears_free_in x (tabs y T11 t12)
(* FILL IN HERE *)
.
Hint Constructors appears_free_in.
Lemma context_invariance : ∀Γ Γ' t S,
Γ ⊢ t ∈ S →
(∀x, appears_free_in x t → Γ x = Γ' x) →
Γ' ⊢ t ∈ S.
Proof with eauto.
intros. generalize dependent Γ'.
has_type_cases (induction H) Case;
intros Γ' Heqv...
Case "T_Var".
apply T_Var... rewrite ← Heqv...
Case "T_Abs".
apply T_Abs... apply IHhas_type. intros y Hafi.
unfold extend.
destruct (eq_id_dec x y)...
(* FILL IN HERE *)
Qed.
Lemma free_in_context : ∀x t T Γ,
appears_free_in x t →
Γ ⊢ t ∈ T →
∃T', Γ x = Some T'.
Proof with eauto.
intros x t T Γ Hafi Htyp.
has_type_cases (induction Htyp) Case; inversion Hafi; subst...
Case "T_Abs".
destruct IHHtyp as [T' Hctx]... ∃T'.
unfold extend in Hctx.
rewrite neq_id in Hctx...
(* FILL IN HERE *)
Qed.
Lemma substitution_preserves_typing : ∀Γ x U v t S,
(extend Γ x U) ⊢ t ∈ S →
empty ⊢ v ∈ U →
Γ ⊢ ([x:=v]t) ∈ S.
Proof with eauto.
(* Theorem: If Gamma,x:U |- t : S and empty |- v : U, then
Gamma |- x:=vt : S. *)
intros Γ x U v t S Htypt Htypv.
generalize dependent Γ. generalize dependent S.
(* Proof: By induction on the term t. Most cases follow directly
from the IH, with the exception of tvar and tabs.
The former aren't automatic because we must reason about how the
variables interact. *)
t_cases (induction t) Case;
intros S Γ Htypt; simpl; inversion Htypt; subst...
Case "tvar".
simpl. rename i into y.
(* If t = y, we know that
empty ⊢ v : U and
Γ,x:U ⊢ y : S
and, by inversion, extend Γ x U y = Some S. We want to
show that Γ ⊢ [x:=v]y : S.
There are two cases to consider: either x=y or x≠y. *)
destruct (eq_id_dec x y).
SCase "x=y".
(* If x = y, then we know that U = S, and that [x:=v]y = v.
So what we really must show is that if empty ⊢ v : U then
Γ ⊢ v : U. We have already proven a more general version
of this theorem, called context invariance. *)
subst.
unfold extend in H1. rewrite eq_id in H1.
inversion H1; subst. clear H1.
eapply context_invariance...
intros x Hcontra.
destruct (free_in_context _ _ S empty Hcontra) as [T' HT']...
inversion HT'.
SCase "x≠y".
(* If x ≠ y, then Γ y = Some S and the substitution has no
effect. We can show that Γ ⊢ y : S by T_Var. *)
apply T_Var... unfold extend in H1. rewrite neq_id in H1...
Case "tabs".
rename i into y. rename t into T11.
(* If t = tabs y T11 t0, then we know that
Γ,x:U ⊢ tabs y T11 t0 : T11→T12
Γ,x:U,y:T11 ⊢ t0 : T12
empty ⊢ v : U
As our IH, we know that forall S Gamma,
Γ,x:U ⊢ t0 : S → Γ ⊢ [x:=v]t0 : S.
We can calculate that
x:=vt = tabs y T11 (if beq_id x y then t0 else x:=vt0)
And we must show that Γ ⊢ [x:=v]t : T11→T12. We know
we will do so using T_Abs, so it remains to be shown that:
Γ,y:T11 ⊢ if beq_id x y then t0 else [x:=v]t0 : T12
We consider two cases: x = y and x ≠ y.
*)
apply T_Abs...
destruct (eq_id_dec x y).
SCase "x=y".
(* If x = y, then the substitution has no effect. Context
invariance shows that Γ,y:U,y:T11 and Γ,y:T11 are
equivalent. Since the former context shows that t0 : T12, so
does the latter. *)
eapply context_invariance...
subst.
intros x Hafi. unfold extend.
destruct (eq_id_dec y x)...
SCase "x≠y".
(* If x ≠ y, then the IH and context invariance allow us to show that
Γ,x:U,y:T11 ⊢ t0 : T12 =>
Γ,y:T11,x:U ⊢ t0 : T12 =>
Γ,y:T11 ⊢ [x:=v]t0 : T12 *)
apply IHt. eapply context_invariance...
intros z Hafi. unfold extend.
destruct (eq_id_dec y z)...
subst. rewrite neq_id...
(* FILL IN HERE *)
Qed.
Theorem preservation : ∀t t' T,
empty ⊢ t ∈ T →
t ⇒ t' →
empty ⊢ t' ∈ T.
Proof with eauto.
intros t t' T HT.
(* Theorem: If empty ⊢ t : T and t ⇒ t', then empty ⊢ t' : T. *)
remember (@empty ty) as Γ. generalize dependent HeqGamma.
generalize dependent t'.
(* Proof: By induction on the given typing derivation. Many cases are
contradictory (T_Var, T_Abs). We show just the interesting ones. *)
has_type_cases (induction HT) Case;
intros t' HeqGamma HE; subst; inversion HE; subst...
Case "T_App".
(* If the last rule used was T_App, then t = t1 t2, and three rules
could have been used to show t ⇒ t': ST_App1, ST_App2, and
ST_AppAbs. In the first two cases, the result follows directly from
the IH. *)
inversion HE; subst...
SCase "ST_AppAbs".
(* For the third case, suppose
t1 = tabs x T11 t12
and
t2 = v2.
We must show that empty ⊢ [x:=v2]t12 : T2.
We know by assumption that
empty ⊢ tabs x T11 t12 : T1→T2
and by inversion
x:T1 ⊢ t12 : T2
We have already proven that substitution_preserves_typing and
empty ⊢ v2 : T1
by assumption, so we are done. *)
apply substitution_preserves_typing with T1...
inversion HT1...
(* FILL IN HERE *)
Qed.
☐ 
End STLCExtended.
(* $Date: 2013-07-17 16:19:11 -0400 (Wed, 17 Jul 2013) $ *)