-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
-
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
-
101
-
102
-
103
-
104
-
105
-
106
-
107
-
108
-
109
-
110
-
111
-
112
-
113
-
114
-
115
-- Simply typed lambda calculus in Lean!
/-- We use Greek letters for variables with type `Typ` -/
inductive Typ
/-- New named type -/
| new (x : Nat)
/-- Function type -/
| fn (α β : Typ)
deriving BEq, ReflBEq, LawfulBEq
inductive Term
/-- Variable -/
| var (x : Nat)
/-- Lambda with de Bruijin indices -/
| lam (b : Term)
/-- Function application -/
| app (f a : Term) (α : Typ)
/-- The type checker! The variable names are chosen so that i.e. `a : Term` corresponds to `α : Typ`. -/
def check (env : List Typ) : Term → Typ → Bool
| .var x, α =>
if _ : x < env.length then env[x] == α else false
| .lam b, .fn α β =>
check (α :: env) b β
| .app f a α, β =>
check env f (.fn α β) && check env a α
| _, _ =>
false
/-- Increment free variables by `k` -/
def incr (k d : Nat) : Term → Term
| .var x =>
.var (if d ≤ x then x + k else x)
| .lam b =>
.lam (incr k (d + 1) b)
| .app f a α =>
.app (incr k d f) (incr k d a) α
/-- `incr` preserves type -/
theorem check_incr env' env'' (h : check (env' ++ env) t τ) (hk : env''.length = k)
: check (env' ++ (env'' ++ env)) (incr k env'.length t) τ := by
match t, τ with
| .var x, α =>
grind [check, incr]
| .lam b, .fn α β =>
simp [check] at h
simpa [check, incr] using check_incr (α :: env') env'' h hk
| .app f a α, β =>
simp [check] at h
simpa [check, incr] using ⟨check_incr env' env'' h.1 hk, check_incr env' env'' h.2 hk⟩
/-- `incr 0` does nothing -/
theorem incr_zero : incr 0 d s = s := by
cases s <;> simp [incr]
· exact incr_zero
· exact ⟨incr_zero, incr_zero⟩
/-- Substitute `s` at index `n` in a term -/
def sub (n : Nat) (s : Term) : Term → Term
| .var x =>
if x == n then incr n 0 s else .var (if n < x then x - 1 else x)
| .lam b =>
.lam (sub (n + 1) s b)
| .app f a α =>
.app (sub n s f) (sub n s a) α
/-- `sub` preserves type -/
theorem check_sub env' (h : check (env' ++ σ :: env) t τ) (hs : check env s σ) (hs' : check (env' ++ env) (incr env'.length 0 s) σ)
: check (env' ++ env) (sub env'.length s t) τ := by
match t, τ with
| .var x, α =>
grind [check, sub]
| .lam b, .fn α β =>
simp [check] at h
simpa [check, sub] using check_sub (α :: env') h hs (check_incr [] (α :: env') hs (by grind))
| .app f a α, β =>
simp [check] at h
simpa [check, sub] using ⟨check_sub env' h.1 hs hs', check_sub env' h.2 hs hs'⟩
/-- Eval without worrying about types -/
partial def eval_untyped : Term → Term
| .var x =>
.var x
| .lam b =>
.lam (eval_untyped b)
| .app f a α =>
let a' := eval_untyped a
match eval_untyped f with
| .lam b => eval_untyped (sub 0 a' b)
| x => .app x a' α
/--
Eval a well-typed expression
TODO: Prove this terminates, see https://cecchetti.sites.cs.wisc.edu/cs704/2025fa/notes/lec23-normalization.pdf
-/
def eval env t τ (h : check env t τ) : { t' // check env t' τ } :=
match t, τ with
| .var x, α =>
⟨.var x, h⟩
| .lam b, .fn α β =>
let ⟨b', hb⟩ := eval (α :: env) b β h
⟨.lam b', by grind [check]⟩
| .app f a α, β =>
let ⟨a', ha⟩ := eval env a α (by grind [check])
let ⟨f', hf⟩ := eval env f (.fn α β) (by grind [check])
match f' with
| .lam b =>
eval env (sub 0 a' b) β (by
simp [check] at hf
exact check_sub [] hf (by grind) (by grind [incr_zero]))
| x =>
⟨.app x a' α, by simpa [check] using ⟨hf, ha⟩⟩
decreasing_by
all_goals sorry