k ::= type | k -> k
c, τ ::= α | c -> c | ∀α:k.c | λα:k.c | c c
e ::= x | λx:c.e | e e | Λα:k.e | e[c]
α : k |- c : k'
----------------- tf-lambda
λα:k.c : k -> k'
c1 : type
c2 : type
----------------- tf-arrow
c1 -> c2 : type
c1 : k -> k'
c2 : k
----------------- tf-app
c1 c2 : k'
α : k |- c : type
----------------- tf-forall
∀α:k.c : type
c : type
x : c |- e : c'
----------------- t-lam
λx:c.e : c -> c'
e1 : c -> c'
e2 : c
----------------- t-app
e1 e2 : c'
α : k |- e : c
----------------- t-poly
Λα:k.e : ∀α:k.c
e : ∀α.c
c': type
----------------- t-tapp
e[c'] : [c'/α]c
----------------- v-lam
λc:k.e value
----------------- v-lam
λc:k.e value
And five rules defining e -> e'...
e1 -> e'1
----------------------- e-app1
e1 e2 -> e'1 e2
e1 value
e2 -> e'2
----------------------- e-app2
e1 e2 -> e1 e'2
e2 value
----------------------- e-applam
(λx:c.e0) e2 -> [e2/x]e0
e -> e'
----------------------- e-tapp
e[c] -> e[c']
----------------------- e-tapp-poly
(Λα:k.e0)[c] -> [c'/α]e0
--------------------- hyp --------------- tf-int
Γ |- α : type -> type Γ |- int : type
----------------------------------------- tf-app ---------------- t-int
Γ |- α int : type -> type Γ' |- 12 : int
-------------------------------------------------------------------- t-lam
α : type -> type |- λx:α int.12 : α int -> int
--------------------------------------------------------------- t-poly
Λα : type -> type. λx:α int. 12 : ∀α:type->type. α int -> int
However, trouble arises if we try to typecheck e[λβ:type.β]5, because we get that e[λβ:type.β]:(λβ:type.β) int -> int, but we WANT this term to have the type int -> int so that we can apply it to 5. Why do we know that (λβ:type.β) int -> int is equivalent to int -> int? We will define a notion of type equivalence c ≡ c : k, and add a typing rule:
e : c'
c ≡ c' : type
-------------- t-equiv
e : c
Here are (some of) the rules for the judgment c ≡ c : k.
c : k
----------------------- te-refl
c ≡ c : k
c1 ≡ c2 : k
----------------------- te-symm
c1 ≡ c2 : k
c1 ≡ c2 : k
c2 ≡ c3 : k
----------------------- te-trans
c1 ≡ c3 : k
c1 ≡ c'1 : k -> k'
c2 ≡ c'2 : k'
----------------------- te-app
c1 c2 ≡ c'1 c'2 : k'
c1 : Type
c2 : Type
----------------------- te-arrow
c1 -> c2 : Type
etc, etc
This is the important rule, it actually talks about computation:
α : k |- c0 : k'
c2 : k
--------------------------- te-trans
(λα:k.c0) c2 ≡ [c2/α]c0 : k'
If Γ |- e : c then Γ |- c : type.
If Γ |- c1 ≡ c2 : k, then Γ |- c1 : k and Γ |- c2 : k
You should be able to prove this!
Canonical forms is no longer trivial, it is an inductive proof. Furthermore, in order for the induction to go to, we need to know that ∀αk.c ≡ c1 -> c2 can never happen, because those two types have different canonical forms. Quote from Karl: "The heart of canonical forms is consistency."
Lemma (Consistency) It is not the case that
c1 -> c2 ≡ ∀α:k.c.
Big idea: you need to make sure you can't go through transitivity
to make some big mess. The proof works because of a few ideas
that we won't go into in the class: look up
confluence, the Church-Rosser Theorem, and
term rewriting theory if you are interested in knowing more.
Lemma (Canonical Forms)