Examples of Dependent Types using Definitions
Combinator Definitions
import Mathlib.Tactic
abbrev K₀.{m, n} := ∀ (α : Type m) (β : Type n) (_x : α) (_y : β), α
def K₁ : K₀ := fun _α _β x _y => x
abbrev S₀.{m, n, o} := ∀ (α : Type m) (β : Type n) (γ : Type o) (_x : α → β → γ) (_y : α → β) (_z : α), γ
def S₁ : S₀ := fun _α _β _γ x y z => x z (y z)
\(M\) combinator
Normal \(\Pi\) typing:
\(\Pi (x : A), B x\)
M
is basically the I
combinator, but it allows reflection at runtime.
We can derive a few useful higher-typing combinator using this base combinator:
$$ M\ (e : t) = t \\ M_{\circ}\ (e : t) = e\ t = e\ (M\ e) = S\ (SKS)\ M\ e \\ M_{s}\ (e : t)\ arg = e\ arg\ (t\ arg) = S\ e\ t\ arg = S\ e\ (M\ e)\ arg = S\ S\ M\ e\ arg $$
Notably, this allows us to fully eliminate binders in all types.
$$ K : K \\ (K \mathbb{N} : K (M \mathbb{N})) $
However, we quickly run into issues. We shouldn't be able to produce an expression of type False
.
$$ false := SK \\ ? : false \\ false : (M S) (M K) \rightarrow false : S K \rightarrow false : false \\ \textbf{contradiction.} $$
However, if we introduce type universes, we easily get around this paradox:
$$ K_{0} : K_{1} \\ M K_{0} = K{1} $$
begin mutual
inductive M where
| mk : ℕ → M
deriving Repr
inductive S where
| mk : ℕ → S
deriving DecidableEq, Repr, BEq
inductive K where
| mk : ℕ → K
deriving DecidableEq, Repr, BEq
inductive Call where
| mk : Expr → Expr → Call
deriving DecidableEq, Repr, BEq
inductive Expr where
| m : M → Expr
| k : K → Expr
| s : S → Expr
| call : Call → Expr
deriving DecidableEq, Repr, BEq
end
namespace Call
def lhs : Call → Expr
| .mk lhs _ => lhs
def rhs : Call → Expr
| .mk _ rhs => rhs
end Call
DSL
We make use of a small DSL for legibility.
declare_syntax_cat skmexpr
syntax "K" ident : skmexpr
syntax "S" ident : skmexpr
syntax "M" ident : skmexpr
syntax "K" num : skmexpr
syntax "S" num : skmexpr
syntax "M" num : skmexpr
syntax "(" skmexpr skmexpr ")" : skmexpr
syntax ident : skmexpr
syntax "(" skmexpr ")" : skmexpr
syntax " ⟪ " skmexpr " ⟫ " : term
macro_rules
| `(⟪ K $u:ident ⟫) => `(Expr.k (.mk $u))
| `(⟪ S $u:ident ⟫) => `(Expr.s (.mk $u))
| `(⟪ M $u:ident ⟫) => `(Expr.m (.mk $u))
| `(⟪ K $u:num ⟫) => `(Expr.k (.mk $u))
| `(⟪ S $u:num ⟫) => `(Expr.s (.mk $u))
| `(⟪ M $u:num ⟫) => `(Expr.m (.mk $u))
| `(⟪ $e:ident ⟫) => `($e)
| `(⟪ ($e:skmexpr) ⟫) => `(⟪$e⟫)
| `(⟪ ($e₁:skmexpr $e₂:skmexpr) ⟫) => `(Expr.call (.mk ⟪ $e₁ ⟫ ⟪ $e₂ ⟫))
syntax "SKM[ " skmexpr " ] " : term
syntax "SKC[" skmexpr skmexpr "]" : term
macro_rules
| `(SKM[ $e:skmexpr ]) => `(⟪ $e ⟫)
macro_rules
| `(SKC[ $e₁:skmexpr $e₂:skmexpr ]) => `(Call.mk ⟪ $e₁ ⟫ ⟪ $e₂ ⟫)
namespace Expr
def max_universe (e : Expr) : ℕ :=
match e with
| SKM[(K n)] => n
| SKM[(S n)] => n
| SKM[(M n)] => n
| SKM[(lhs rhs)] =>
(max_universe lhs) + (max_universe rhs)
def min_universe (e : Expr) : ℕ :=
match e with
| SKM[(K n)] => n
| SKM[(S n)] => n
| SKM[(M n)] => n
| SKM[(lhs rhs)] =>
min (max_universe lhs) (max_universe rhs)
inductive valid_universes : Expr → Prop
| k : valid_universes SKM[(K n)]
| s : valid_universes SKM[(S n)]
| m : valid_universes SKM[(M n)]
| call : lhs.max_universe > rhs.max_universe
→ valid_universes lhs
→ valid_universes rhs
→ valid_universes SKM[(lhs rhs)]
end Expr
Judgment Rules
K
, M
, and S
must be well-typed on their own. We need some expression that can represent the type of K
. One option is the \(\rightarrow\) type constructor from the SLTC or System F. The typing of \(S\) and \(K\) are fairly obvious. However, the typing of \(M\) is not. It's not immediately clear that we need typing judgment rules for K
, M
, or S
alone. There is no way to explicitly assign a type to K
, M
, or S
. They just are. However, there are emergent rules for application. The typical rules for application in the SK combinator calculus are as follows:
$$ K : \alpha \rightarrow \beta \rightarrow \alpha \\ S : (\alpha \rightarrow \beta \rightarrow \gamma) \rightarrow (\alpha \rightarrow \beta) \rightarrow \alpha \rightarrow \gamma \\ M : hmm $$
Typing rules quickly beome complicated. We would prefer to avoid binders in our type discipline. We can create an "emergent" typing based on function application.
$$ \frac{ \Gamma \vdash e_{1} : T }{ \Gamma \vdash K\ e_{1}\ e_{2} : T } $$ $$ \frac{ \Gamma \vdash f_{1}\ \text{arg}\ (f_{2}\ \text{arg}) : T) }{ \Gamma \vdash S\ f_{1}\ f_{2}\ \text{arg} : T } $$
But what is the type of \(K\)? It's not really obvious. Clearly, we need some way to represent the type of \(K\) that avoids binders. Recall that:
$$ Kxy = x $$
The type of \(K\) is typically \(\alpha \rightarrow \beta \rightarrow \alpha\). Its type is of the form of the \(K\) combinator. We can thus define a typing as such:
$$ K : K $$
This typing is coherent under an evaluation rule for the \(M\) combinator:
$$ M\ (x : t)\ \text{arg} = \text{arg}\ x\ (\text{arg}\ t) \\ M\ \mathbb{N}\ K = K\ \mathbb{N}\ (K\ \text{Type}) = \mathbb{N} \\ M\ K\ K = K\ K\ (K\ K) = K $$
mutual
inductive is_eval_once : Expr → Expr → Prop
| k x y n : is_eval_once SKM[((K n x) y)] x
| s x y z n : is_eval_once SKM[(((S n x) y) z)] SKM[((x z) (y z))]
| m e t n : valid_judgment e t
→ is_eval_once SKM[((M n) e)] t
| left : is_eval_once lhs lhs'
→ is_eval_once SKM[(lhs rhs)] SKM[(lhs' rhs)]
| right : is_eval_once rhs rhs'
→ is_eval_once SKM[(lhs rhs)] SKM[(lhs rhs')]
| k_rfl : is_eval_once SKM[K n] SKM[K n]
| m_rfl : is_eval_once SKM[M n] SKM[M n]
| s_rfl : is_eval_once SKM[S n] SKM[S n]
inductive is_eval_once_weak : Expr → Expr → Prop
| k x y n : is_eval_once_weak SKM[((K n x) y)] x
| s x y z n : is_eval_once_weak SKM[(((S n x) y) z)] SKM[((x z) (y z))]
| m e t n : valid_judgment_weak e t
→ is_eval_once_weak SKM[((M n) e)] t
| left : is_eval_once_weak lhs lhs'
→ is_eval_once_weak SKM[(lhs rhs)] SKM[(lhs' rhs)]
| right : is_eval_once_weak rhs rhs'
→ is_eval_once_weak SKM[(lhs rhs)] SKM[(lhs rhs')]
| k_rfl : is_eval_once_weak SKM[K n] SKM[K n]
| m_rfl : is_eval_once_weak SKM[M n] SKM[M n]
| s_rfl : is_eval_once_weak SKM[S n] SKM[S n]
inductive beta_eq : SkExpr → SkExpr → Prop
| rfl : beta_eq e e
| eval : is_eval_once e₁ e₂ → beta_eq e₁ e₂
| left : beta_eq lhs lhs' → beta_eq SKM[(lhs rhs)] SKM[(lhs' rhs)]
| right : beta_eq rhs rhs' → beta_eq SKM[(lhs rhs)] SKM[(lhs rhs')]
| symm : beta_eq e₂ e₁ → beta_eq e₁ e₂
| trans : beta_eq e₁ e₂ → beta_eq e₂ e₃ → beta_eq e₁ e₃
inductive valid_judgment : Expr → Expr → Prop
| k n : valid_judgment SKM[K n] (.k (.mk n.succ))
| s n : valid_judgment SKM[S n] (.s (.mk n.succ))
| m n : valid_judgment SKM[M n] (.m (.mk n.succ))
| call lhs rhs : lhs.max_universe > rhs.max_universe
→ valid_judgment lhs (.call (.mk (Expr.m (.mk lhs.max_universe.succ)) lhs))
→ valid_judgment rhs (.call (.mk (Expr.m (.mk rhs.max_universe.succ)) rhs))
→ valid_judgment SKM[(lhs rhs)]
(.call (.mk
(.call (.mk
(Expr.m (.mk lhs.max_universe.succ))
lhs
))
(.call (.mk
(Expr.m (.mk rhs.max_universe.succ))
rhs
))
))
| beta_eq e t₁ t₂ : valid_judgment e t₁ → beta_eq t₁ t₂ → valid_judgment e t₂
inductive valid_judgment_weak : Expr → Expr → Prop
| k n : valid_judgment_weak SKM[K n] (.k (.mk n.succ))
| s n : valid_judgment_weak SKM[S n] (.s (.mk n.succ))
| m n : valid_judgment_weak SKM[M n] (.m (.mk n.succ))
| call lhs rhs : lhs.max_universe > rhs.max_universe
→ valid_judgment_weak lhs (.call (.mk (Expr.m (.mk lhs.max_universe.succ)) lhs))
→ valid_judgment_weak rhs (.call (.mk (Expr.m (.mk rhs.max_universe.succ)) rhs))
→ valid_judgment_weak SKM[(lhs rhs)]
(.call (.mk
(.call (.mk
(Expr.m (.mk lhs.max_universe.succ))
lhs
))
(.call (.mk
(Expr.m (.mk rhs.max_universe.succ))
rhs
))
))
end
inductive is_normal_n : ℕ → Expr → Expr → Prop
| one : is_eval_once e e → is_normal_n 1 e e
| succ : is_eval_once e e' → is_normal_n n e' e_final → is_normal_n n.succ e e_final
mutual
partial def type_of (e : Expr) : Option Expr :=
match e with
| SKM[(K n)] => pure $ (.k (.mk n.succ))
| SKM[(S n)] => pure $ (.s (.mk n.succ))
| SKM[(M n)] => pure $ (.m (.mk n.succ))
| SKM[(lhs rhs)] => do
if rhs.max_universe ≥ lhs.max_universe then
none
else
eval_unsafe (.call (.mk (← eval_unsafe (.call (.mk (.m (.mk lhs.max_universe.succ)) lhs))) (← eval_unsafe (.call (.mk (.m (.mk rhs.max_universe.succ)) rhs)))))
partial def eval_unsafe (e : Expr) : Option Expr :=
match e with
| SKM[(((K _n) x) _y)] => eval_unsafe x
| SKM[((((S _n) x) y) z)] => eval_unsafe SKM[((x z) (y z))]
| SKM[(M _n e)] => do eval_unsafe (← type_of e)
| SKM[(lhs rhs)] => do
let lhs' ← eval_unsafe lhs
if SKM[(lhs' rhs)] = SKM[(lhs rhs)] then
some SKM[(lhs rhs)]
else
eval_unsafe SKM[(lhs' rhs)]
| x => x
partial def eval_once (e : Expr) : Option Expr :=
match e with
| SKM[(((K _n) x) _y)] => x
| SKM[((((S _n) x) y) z)] => SKM[((x z) (y z))]
| SKM[(M _n e)] => type_of e
| SKM[(lhs rhs)] => do
let lhs' ← eval_once lhs
SKM[(lhs' rhs)]
| x => x
end
Strong Normalization
A strong proof of consistency involves proving that every well-typed expression terminates. I do so. I utilize the typical reducibility candidates strategy.
inductive sn : Expr → Prop
| trivial : is_eval_once e e → sn e
| hard : is_eval_once e e' → sn e' → sn e
Note that we define evaluation as a relation on expressions. This is due to eval_once
's dependence on the type of e
. This appears problematic and confusing. However, we can still prove membership in R of all well-typed expressions.
lemma k_eval_sn : ∀ n x y, sn x → sn SKM[((K n x) y)] := by
intro n x y sn_x
apply sn.hard
apply is_eval_once.k
exact sn_x
lemma s_eval_sn : ∀ n x y z, sn SKM[((x z) (y z))] → sn SKM[(((S n x) y) z)] := by
intro n x y z sn_eval
apply sn.hard
apply is_eval_once.s
exact sn_eval
Type Preservation
To prove preservation, we prove that we can derive typings for (K (x : α) y : α)
from our base typing rules using M
.
Note that our typing rules make extensive use of the M
combinator for "reflection." We can use an S
-transformation rule like such:
$$ (e_{1}\ e_{2} : M\ e_{1}\ e_{2}) \implies (e_{1}\ e_{2} : (M\ e_{1})\ (M\ e_{2})) $$
This shortens our type preservation proof significantly by allowing us to collapse expressions of the form \((M K arg)\) to reducible \((M K) (M arg)\) expressions.
lemma valid_judgment_imp_m : ∀ n, valid_judgment e t → valid_judgment e SKM[((M n) e)] := by
intro n h
apply valid_judgment.beta_eq
exact h
apply beta_eq.symm
apply beta_eq.eval
apply is_eval_once.m
exact h
lemma congr_m : valid_judgment a t
→ valid_judgment b t
→ beta_eq a b
→ beta_eq (Expr.call (.mk (.m (.mk a.max_universe.succ)) a)) (.call (.mk (.m (.mk b.max_universe.succ)) b)) := by
intro h_t_lhs h_t_rhs h
simp [Expr.max_universe]
cases h_t_lhs
cases h_t_rhs
cases h
apply beta_eq.rfl
apply beta_eq.rfl
apply beta_eq.rfl
apply beta_eq.rfl
cases h
apply beta_eq.rfl
case k.beta_eq.eval rhs h₂ h₃ h₄ h₅ =>
simp [Expr.max_universe] at *
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
apply valid_judgment.k
simp
apply beta_eq.symm
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
exact h₃
exact h₄
case k.beta_eq.symm rhs h₂ h₃ h₄ h₅ =>
simp [Expr.max_universe] at *
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
apply valid_judgment.k
simp
apply beta_eq.symm
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
exact h₃
exact h₄
case k.beta_eq.trans rhs h_rhs h₂ h₃ h₄ h₅ =>
apply beta_eq.trans
apply beta_eq.right
exact h₄
apply beta_eq.trans
apply beta_eq.right
exact h₅
simp [Expr.max_universe]
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
exact h_rhs
apply beta_eq.symm
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
exact h_rhs
apply beta_eq.rfl
case s =>
apply beta_eq.trans
apply beta_eq.right
exact h
simp [Expr.max_universe]
apply beta_eq.symm
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
exact h_t_rhs
simp
apply beta_eq.symm
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
exact h_t_rhs
simp
exact beta_eq.rfl
case m =>
apply beta_eq.trans
apply beta_eq.right
exact h
simp [Expr.max_universe]
apply beta_eq.symm
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
exact h_t_rhs
simp
apply beta_eq.symm
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
exact h_t_rhs
simp
exact beta_eq.rfl
case call lhs rhs h_u h_t_lhs h_t_rhs =>
simp [Expr.max_universe] at *
apply beta_eq.trans
apply beta_eq.right
exact h
apply beta_eq.symm
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
exact h_t_rhs
apply beta_eq.symm
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
exact h_t_rhs
apply beta_eq.rfl
case beta_eq t' h_t' h_eq =>
have h_t_lhs'' := valid_judgment.beta_eq _ _ _ h_t' h_eq
have h_t_rhs'' := valid_judgment.beta_eq _ _ _ h_t_rhs (beta_eq.symm h_eq)
apply beta_eq.trans
apply beta_eq.right
exact h
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
exact h_t_rhs''
apply beta_eq.symm
apply beta_eq.eval
apply is_eval_once.m
exact h_t_rhs''
lemma eval_preserves_judgment : ∀ e e' t, valid_judgment e t → is_eval_once e e' → valid_judgment e' t' → valid_judgment e' t := by
intro c e' t h_t h_eval h_t'
cases h_eval
case k y n =>
apply valid_judgment.beta_eq
apply valid_judgment_imp_m (e'.max_universe.succ)
exact h_t'
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
exact h_t'
apply beta_eq.symm
apply beta_eq.trans
apply beta_eq.symm
apply beta_eq.eval
apply is_eval_once.m
use 0
exact h_t
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.right
apply is_eval_once.k
apply beta_eq.eval
apply is_eval_once.m
exact h_t'
case s x y z n =>
apply valid_judgment.beta_eq
apply valid_judgment_imp_m (SKM[((x z) (y z))].max_universe.succ)
exact h_t'
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
exact h_t'
apply beta_eq.symm
apply beta_eq.trans
apply beta_eq.symm
apply beta_eq.eval
apply is_eval_once.m
use 0
exact h_t
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.right
apply is_eval_once.s
apply beta_eq.eval
apply is_eval_once.m
exact h_t'
case m e'' n h =>
apply valid_judgment.beta_eq
apply valid_judgment_imp_m (e''.max_universe.succ)
exact h_t'
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
exact h_t'
apply beta_eq.symm
apply beta_eq.trans
apply beta_eq.symm
apply beta_eq.eval
apply is_eval_once.m
use 0
exact h_t
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.right
apply is_eval_once.m
exact h
apply beta_eq.eval
apply is_eval_once.m
exact h_t'
case left lhs lhs' rhs h_eq =>
apply valid_judgment.beta_eq
apply valid_judgment_imp_m
use 0
exact h_t'
apply beta_eq.trans
apply beta_eq.right
apply beta_eq.left
apply beta_eq.symm
apply beta_eq.eval h_eq
apply beta_eq.eval
apply is_eval_once.m
exact h_t
case right rhs rhs' lhs h_eq =>
apply valid_judgment.beta_eq
apply valid_judgment_imp_m
use 0
exact h_t'
apply beta_eq.trans
apply beta_eq.right
apply beta_eq.right
apply beta_eq.symm
apply beta_eq.eval h_eq
apply beta_eq.eval
apply is_eval_once.m
exact h_t
case k_rfl =>
exact h_t
case s_rfl =>
exact h_t
case m_rfl =>
exact h_t
lemma valid_judgment_call_imp_n_bounds : valid_judgment_weak SKM[(lhs rhs)] t → lhs.max_universe > rhs.max_universe := by
intro h
cases h
case call _ _ h_u =>
exact h_u
lemma valid_judgment_weak_imp_valid_judgment : valid_judgment_weak e t → valid_judgment e t := by
intro h
cases h
apply valid_judgment.k
apply valid_judgment.s
apply valid_judgment.m
apply valid_judgment.call
case call.a lhs rhs h_u _ _ =>
exact h_u
case call.a lhs rhs h_u h_t_lhs h_t_rhs =>
apply valid_judgment_weak_imp_valid_judgment
exact h_t_lhs
case call.a lhs rhs h_u h_t_lhs h_t_rhs =>
apply valid_judgment_weak_imp_valid_judgment
exact h_t_rhs
lemma valid_judgment_call_imp_judgment_lhs_rhs_easy : valid_judgment_weak SKM[(lhs rhs)] t → (∃ t_lhs, valid_judgment_weak lhs t_lhs) ∧ (∃ t_rhs, valid_judgment_weak rhs t_rhs) := by
intro h_t
cases h_t
case call h_t_lhs h_t_rhs h_u =>
constructor
use (.call (.mk (.m (.mk lhs.max_universe.succ)) lhs))
use (.call (.mk (.m (.mk rhs.max_universe.succ)) rhs))
lemma valid_judgment_call_imp_judgment_lhs_rhs : valid_judgment SKM[(lhs rhs)] t → (∃ t_lhs, valid_judgment lhs t_lhs) ∧ (∃ t_rhs, valid_judgment rhs t_rhs) := by
intro h_t
cases h_t
case call h_t_lhs h_t_rhs h_u =>
constructor
use (.call (.mk (.m (.mk lhs.max_universe.succ)) lhs))
use (.call (.mk (.m (.mk rhs.max_universe.succ)) rhs))
case beta_eq t₂ h_t₂ h_t_beq =>
have h_t₃ := valid_judgment.beta_eq _ _ _ h_t₂ h_t_beq
constructor
cases h_t₂
case left.call =>
use (.call (.mk (.m (.mk lhs.max_universe.succ)) lhs))
case left.beta_eq =>
sorry
lemma valid_judgment_imp_valid_universes : valid_judgment_weak e t → e.valid_universes := by
intro h_t
cases h_t
apply Expr.valid_universes.k
apply Expr.valid_universes.s
apply Expr.valid_universes.m
case call lhs rhs h_u h_t_lhs h_t_rhs =>
apply Expr.valid_universes.call
exact h_u
apply valid_judgment_imp_valid_universes at h_t_lhs
exact h_t_lhs
apply valid_judgment_imp_valid_universes at h_t_rhs
exact h_t_rhs
lemma weakening : valid_judgment_weak e t → valid_judgment e t := by
intro h
cases h
case k =>
apply valid_judgment.k
case s =>
apply valid_judgment.s
case m =>
apply valid_judgment.m
case call lhs rhs h_u h_t_lhs h_t_rhs =>
apply valid_judgment.call
exact h_u
apply weakening at h_t_lhs
exact h_t_lhs
apply weakening at h_t_rhs
exact h_t_rhs
lemma eval_preserves_judgment_hard : ∀ e e' t, valid_judgment_weak e t → is_eval_once e e' → valid_judgment_weak e' t := by
intro e e' t h_t h_eval
cases h_t
case k n =>
cases h_eval
apply valid_judgment_weak.k
case s =>
cases h_eval
apply valid_judgment_weak.s
case m =>
cases h_eval
apply valid_judgment_weak.m
case call lhs rhs h_u h_t_lhs h_t_rhs =>
match h₀ : h_eval with
| .k lhs' rhs' n =>
cases h_t_lhs
| .s x' y' z' n =>
cases h_t_lhs
| .left h_eval' =>
cases h_t_lhs
| .right h_eval' =>
cases h_t_lhs
| .m e t n h_t =>
cases h_t_lhs
lemma eval_preserves_judgment_hard' : ∀ e e' t, valid_judgment e t → is_eval_once e e' → valid_judgment e' t := by
intro e e' t h_t h_eval
cases h_t
case k n =>
cases h_eval
apply valid_judgment.k
case s =>
cases h_eval
apply valid_judgment.s
case m =>
cases h_eval
apply valid_judgment.m
case call lhs rhs h_u h_t_lhs h_t_rhs =>
match h_e'_eq : e' with
| .m (.mk n) =>
apply valid_judgment.beta_eq
apply valid_judgment.m
apply beta_eq.symm
have h_eval_beq := beta_eq.eval h_eval
apply beta_eq.trans
apply beta_eq.left
apply beta_eq.eval
apply is_eval_once.m
exact h_t_lhs
apply beta_eq.symm
apply beta_eq.trans
apply beta_eq.symm
apply beta_eq.eval
apply is_eval_once.m
use n
apply valid_judgment.m
apply beta_eq.trans
apply beta_eq.right
exact (beta_eq.symm h_eval_beq)
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
apply valid_judgment.call
exact h_u
exact h_t_lhs
exact h_t_rhs
apply beta_eq.rfl
| .k (.mk n) =>
apply valid_judgment.beta_eq
apply valid_judgment.k
apply beta_eq.symm
have h_eval_beq := beta_eq.eval h_eval
apply beta_eq.trans
apply beta_eq.left
apply beta_eq.eval
apply is_eval_once.m
exact h_t_lhs
apply beta_eq.symm
apply beta_eq.trans
apply beta_eq.symm
apply beta_eq.eval
apply is_eval_once.m
use n
apply valid_judgment.k
apply beta_eq.trans
apply beta_eq.right
exact (beta_eq.symm h_eval_beq)
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
apply valid_judgment.call
exact h_u
exact h_t_lhs
exact h_t_rhs
apply beta_eq.rfl
| .s (.mk n) =>
apply valid_judgment.beta_eq
apply valid_judgment.s
apply beta_eq.symm
have h_eval_beq := beta_eq.eval h_eval
apply beta_eq.trans
apply beta_eq.left
apply beta_eq.eval
apply is_eval_once.m
exact h_t_lhs
apply beta_eq.symm
apply beta_eq.trans
apply beta_eq.symm
apply beta_eq.eval
apply is_eval_once.m
use n
apply valid_judgment.s
apply beta_eq.trans
apply beta_eq.right
exact (beta_eq.symm h_eval_beq)
apply beta_eq.trans
apply beta_eq.eval
apply is_eval_once.m
apply valid_judgment.call
exact h_u
exact h_t_lhs
exact h_t_rhs
apply beta_eq.rfl
| .call (.mk lhs' rhs') =>
sorry
lemma all_sn_eval_once : ∀ e, sn e → ∃ e', is_eval_once e e' := by
intro e h
cases h
case trivial h =>
use e
case hard e' h_sn h_step =>
use e'
lemma is_eval_once_rfl : is_eval_once e e → e = e := by
intro h
cases h
case m =>
rfl
case left =>
rfl
case right =>
rfl
case k_rfl =>
rfl
case s_rfl =>
rfl
case m_rfl =>
rfl
theorem all_well_typed_weak_sn (e : Expr) (t : Expr) : valid_judgment_weak e t → sn e := by
intro h_t
match e with
| SKM[((K n x) y)] =>
apply sn.hard
apply is_eval_once.k
apply all_well_typed_weak_sn
apply eval_preserves_judgment_hard
exact h_t
apply is_eval_once.k
| SKM[(((S n x) y) z)] =>
apply sn.hard
apply is_eval_once.s
cases h_t
case a.call h_t_lhs h_t_z h_u =>
cases h_t_lhs
| SKM[(M n e')] =>
cases h_t
case call h_t_m h_t_e' h_t_u =>
apply sn.hard
apply is_eval_once.m
apply weakening at h_t_e'
exact h_t_e'
simp_all
cases h_t_m
| SKM[(lhs rhs)] =>
cases h_t
case call h_t_lhs h_t_rhs h_u =>
cases h_t_lhs
| .k (.mk n) =>
apply sn.trivial
exact is_eval_once.k_rfl
| .s (.mk n) =>
apply sn.trivial
exact is_eval_once.s_rfl
| .m (.mk n) =>
apply sn.trivial
exact is_eval_once.m_rfl
lemma sn_reverse_execution : sn e' → is_eval_once e e' → sn e := by
intro h_sn_eval h
apply sn.hard
exact h
exact h_sn_eval
lemma sn_imp_n_steps_eval_normal (e : Expr) : sn e → ∃ n e', is_normal_n n e e' := by
intro h_sn
induction h_sn
case trivial e h =>
use 1
use e
apply is_normal_n.one
exact h
case hard e'' e''' h_eval' h_sn h_is_step =>
have ⟨n_sub_1, e'''', h_eval⟩ := h_is_step
use n_sub_1.succ
use e''''
apply is_normal_n.succ
cases h_eval
exact h_eval'
exact h_eval'
exact h_eval
def is_candidate_for_weak (e : Expr) (t : Expr) : Prop := valid_judgment_weak e t ∧ e.valid_universes
def is_candidate_for (e : Expr) (t : Expr) : Prop := valid_judgment e t ∧ e.valid_universes
lemma k_eval_def_eq : is_eval_once SKM[(K n)] e → e = SKM[(K n)] := by
intro h
cases h
rfl
lemma s_eval_def_eq : is_eval_once SKM[(S n)] e → e = SKM[(S n)] := by
intro h
cases h
rfl
lemma m_eval_def_eq : is_eval_once SKM[(M n)] e → e = SKM[(M n)] := by
intro h
cases h
rfl
lemma membership_candidate_preserved : valid_judgment_weak e t → is_candidate_for_weak e t → is_eval_once e e' → is_candidate_for_weak e' t := by
intro h_t h_candidate h_eval
have h_candidate₀ := h_candidate
unfold is_candidate_for_weak at h_candidate
have ⟨h_t_e, h_candidate_e⟩ := h_candidate
cases h_candidate_e
case k n =>
apply k_eval_def_eq at h_eval
rw [h_eval]
cases h_t_e
case k =>
exact ⟨valid_judgment_weak.k n, by apply Expr.valid_universes.k⟩
case s n =>
apply s_eval_def_eq at h_eval
rw [h_eval]
cases h_t_e
case s =>
exact ⟨valid_judgment_weak.s n, by apply Expr.valid_universes.s⟩
case m n =>
apply m_eval_def_eq at h_eval
rw [h_eval]
cases h_t_e
case m =>
exact ⟨valid_judgment_weak.m n, by apply Expr.valid_universes.m⟩
case call lhs rhs h₁ h_u_lhs h_u_rhs =>
have h_t_e' := eval_preserves_judgment_hard SKM[(lhs rhs)] e' t h_t_e h_eval
unfold is_candidate_for_weak
constructor
exact h_t_e'
cases e'
case right.m m =>
match m with
| .mk n =>
apply Expr.valid_universes.m
case right.s s =>
match s with
| .mk n =>
apply Expr.valid_universes.s
case right.k k =>
match k with
| .mk n =>
apply Expr.valid_universes.k
case right.call c =>
match c with
| .mk lhs' rhs' =>
have ⟨⟨t_lhs, h_t_lhs'⟩, ⟨t_rhs, h_t_rhs'⟩⟩ := valid_judgment_call_imp_judgment_lhs_rhs_easy h_t_e'
apply Expr.valid_universes.call
apply valid_judgment_call_imp_n_bounds at h_t_e'
exact h_t_e'
simp_all
apply valid_judgment_imp_valid_universes
exact h_t_lhs'
apply valid_judgment_imp_valid_universes
exact h_t_rhs'
theorem all_well_typed_candidate : valid_judgment_weak e t → is_candidate_for_weak e t := by
intro h_t
cases h_t
case k =>
unfold is_candidate_for_weak
constructor
apply valid_judgment_weak.k
apply Expr.valid_universes.k
case s =>
unfold is_candidate_for_weak
constructor
apply valid_judgment_weak.s
apply Expr.valid_universes.s
case m =>
unfold is_candidate_for_weak
constructor
apply valid_judgment_weak.m
apply Expr.valid_universes.m
case call lhs rhs h_u h_t_lhs h_t_rhs =>
unfold is_candidate_for_weak
constructor
apply valid_judgment_weak.call
exact h_u
exact h_t_lhs
exact h_t_rhs
apply Expr.valid_universes.call
exact h_u
apply valid_judgment_imp_valid_universes
exact h_t_lhs
apply valid_judgment_imp_valid_universes
exact h_t_rhs
theorem sum_universes_decrease_normal_form : valid_judgment_weak SKM[(lhs rhs)] t → is_normal_n n SKM[(lhs rhs)] e' → SKM[(lhs rhs)].max_universe > e'.max_universe := by
intro h_t h_normal
cases h_normal
case one =>
contradiction
case succ e_next n h_step h_normal =>
simp_all
if h_n_eq : n = 1 then
simp_all
cases h_normal
contradiction
contradiction
else if h_n_eq : n > 2 then
have h_t_e'' := eval_preserves_judgment_hard SKM[(lhs rhs)] e_next t h_t h_step
have ⟨⟨t_lhs, h_t_lhs⟩, ⟨t_rhs, h_t_rhs⟩⟩ := valid_judgment_call_imp_judgment_lhs_rhs_easy h_t
simp_all
cases e'
case m m =>
simp [Expr.max_universe] at *
cases h_t_e''
cases h_t
cases h_t
cases h_t
case call h_n lhs' rhs' h_u h_t_lhs h_t_rhs =>
cases h_t_lhs
case k =>
simp [Expr.max_universe] at *
cases h_t_e''
cases h_t
cases h_t
cases h_t
case call h_n lhs' rhs' h_u h_t_lhs h_t_rhs =>
cases h_t_lhs
case s =>
simp [Expr.max_universe] at *
cases h_t_e''
cases h_t
cases h_t
cases h_t
case call h_n lhs' rhs' h_u h_t_lhs h_t_rhs =>
cases h_t_lhs
case call =>
simp [Expr.max_universe] at *
cases h_t_e''
cases h_t
cases h_t
cases h_t
case call h_n lhs' rhs' h_u h_t_lhs h_t_rhs =>
cases h_t_lhs
else
simp_all
cases h_step
simp [Expr.max_universe]
contradiction
simp [Expr.max_universe]
contradiction
simp [Expr.max_universe]
contradiction
simp [Expr.max_universe]
contradiction
simp [Expr.max_universe]
contradiction
theorem sum_universes_decrease_normal_form_hard : valid_judgment_weak e t → is_normal_n n e e' → (e.max_universe > e'.max_universe ∨ is_normal_n 1 e e') := by
intro h_t h_normal
induction h_normal
case one e'' h_eval =>
right
apply is_normal_n.one
exact h_eval
case succ e_next e_next_next e_final h_n h_eval_step h_norm h =>
have h_t' := eval_preserves_judgment_hard _ _ _ h_t h_eval_step
simp [h_t'] at h
cases h
case inl h =>
left
cases h_t
cases h_t'
simp_all
cases h_t'
simp_all
cases h_t'
simp_all
case call lhs rhs h_u h_t_lhs h_t_rhs =>
simp [Expr.max_universe] at *
apply lt_trans
exact h
apply sum_universes_decrease_normal_form
apply valid_judgment_weak.call
exact h_u
exact h_t_lhs
exact h_t_rhs
cases h_norm
case hbc.a.one h_eval_next_next =>
apply is_normal_n.succ
exact h_eval_step
apply is_normal_n.one
exact h_eval_next_next
case hbc.a.succ e' n h_eval h_eval' =>
apply is_normal_n.succ
exact h_eval_step
apply is_normal_n.one
contradiction
case inr h =>
have h' := is_normal_n.succ h_eval_step h_norm
cases h_t
cases h_t'
right
exact h
cases h_t'
right
exact h
cases h'
cases h_t'
right
exact h
case m.succ e'' h_eval h_norm =>
cases h_eval_step
simp_all
case call lhs rhs h_u h_t_lhs h_t_rhs =>
simp_all
cases h_t_lhs
theorem all_candidates_sn (e : Expr) : is_candidate_for_weak e t → sn e := by
intro h
unfold is_candidate_for_weak at h
have ⟨h_t, h_u⟩ := h
match h_e_eq : e with
| .k _ =>
apply sn.trivial
cases h_t
apply is_eval_once.k_rfl
| .m _ =>
apply sn.trivial
cases h_t
apply is_eval_once.m_rfl
| .s _ =>
apply sn.trivial
cases h_t
apply is_eval_once.s_rfl
| SKM[(((K n) x) y)] =>
cases h_t
case call h_t_lhs h_t_rhs h_u =>
have x_sn : sn x := by
simp_all
apply valid_judgment_call_imp_judgment_lhs_rhs_easy at h_t_lhs
have ⟨_, ⟨t_x, h_t_x⟩⟩ := h_t_lhs
apply @all_candidates_sn t_x x
unfold is_candidate_for_weak
constructor
exact h_t_x
apply valid_judgment_imp_valid_universes at h_t_x
exact h_t_x
apply sn.hard
apply is_eval_once.k
exact x_sn
| SKM[(lhs rhs)] =>
-- We know that lhs and rhs are both typed.
-- We know as a consequence that both are candidates
-- and by extension, both are sn
have ⟨⟨t_lhs, h_t_lhs⟩, ⟨t_rhs, h_t_rhs⟩⟩ := valid_judgment_call_imp_judgment_lhs_rhs_easy h_t
have candidate_lhs := all_well_typed_candidate h_t_lhs
have candidate_rhs := all_well_typed_candidate h_t_rhs
have sn_lhs := all_candidates_sn _ candidate_lhs
have sn_rhs := all_candidates_sn _ candidate_rhs
-- However, evaluation could potentially produce a new term which is not SN.
-- We know that since the lhs is sn, it has a normal form
-- which is smaller in size than its predecessor
-- or it is rfl
-- We also know that the rhs has a normal form which is smaller.
-- We can use the is_eval_once rules to establish that the left-hand-side
-- evaluates to the next-step in finding a normal form
-- This isn't necessarily smaller in size.
-- This means we will have to use the beta_eq sn rule
-- to "jump" to the normal form
have ⟨n_eval_lhs, lhs', h_eval_lhs⟩ := sn_imp_n_steps_eval_normal lhs sn_lhs
have ⟨n_eval_rhs, rhs', h_eval_rhs⟩ := sn_imp_n_steps_eval_normal rhs sn_rhs
have h_terminal_lhs := sum_universes_decrease_normal_form_hard h_t_lhs h_eval_lhs
have h_terminal_rhs := sum_universes_decrease_normal_form_hard h_t_rhs h_eval_rhs
cases h_terminal_lhs
case inl h =>
cases h_terminal_rhs
case inl h' =>
cases h_eval_lhs
case one h_eval_lhs' =>
contradiction
case succ h_eval_lhs' =>
contradiction
case inr h' =>
contradiction
case inr h =>
contradiction
theorem all_well_typed_sn_weak : ∀ e t, valid_judgment_weak e t → sn e := by
intro e t h_t
have h_candidate := all_well_typed_candidate h_t
have h_sn := all_candidates_sn _ h_candidate
exact h_sn
theorem all_types_decideable : ∀ e t, valid_judgment e t → ∃ e, valid_judgment_weak e t := by
intro e t h_t
cases h_t
case k n =>
use SKM[K n]
apply valid_judgment_weak.k
case s n =>
use SKM[S n]
apply valid_judgment_weak.s
case m n =>
use SKM[M n]
apply valid_judgment_weak.m
case call lhs rhs h_u h_t_lhs h_t_rhs =>
apply all_types_decideable at h_t_lhs
apply all_types_decideable at h_t_rhs
have ⟨lhs', h_t_lhs'⟩ := h_t_lhs
have ⟨rhs', h_t_rhs'⟩ := h_t_rhs
use SKM[(lhs' rhs')]
cases h_t_lhs'
case beta_eq t' h_t' h_eq =>
cases e
case m =>
sorry
case k =>
sorry
case s =>
sorry
case call c =>
match c with
| .mk lhs rhs =>
sorry
theorem all_well_typed_sn : ∀ e t, valid_judgment e t → sn e := by
intro e t h_t
cases h_t
case k =>
apply sn.trivial
apply is_eval_once.k_rfl
case s =>
apply sn.trivial
apply is_eval_once.s_rfl
case m =>
apply sn.trivial
apply is_eval_once.m_rfl
case call lhs rhs h_u h_t_lhs h_t_rhs =>
apply all_well_typed_sn_weak
sorry
Ramblings
M_id (e : t) = t := M
We want:
(K (Nat : Typea) Real : Typea) (K Nat Real : K Typea Typeb) (K Nat Real : M K ) (K Nat Real : M
we can quickly see that we have a more specific version of M that becomes useful:
M_1 (e : t) arg = arg t
We can actually derive this from the existing combinators. I will do this later.
(K Nat Real : K (M_1 Nat) (M_1 Real))
I define SR = S (S (K K) (S K S)) (K (S K S K))
SR M_1 Nat Real = (M_1 Nat) (M_1 Real)
K (SR M_1 Nat Real) = (M_1 Nat) (M_1 Real)
So, we define the typing judgment:
(x : t) (y : t) : x (M_1 x)
Eh this doesn't generalize well. Also, let's take an example. Identify function.
((S : S) (K : K) (S : S)) : S
#check 1 = 1
Made by Dowland Aiello.