Preservation of SN and Typing Judgements
In order to prove inclusion of all well-typed expressions in our reducibility candidate set, we must first prove:
- That typing judgements are preserved after one-step evaluation
- That membership in the candidate set is preserved after evaluation
import SkLean.Ast
import SkLean.Dsl
import SkLean.Typing
import Mathlib.Tactic
import Init.Data.List.Lemmas
Preservation of typing judgements
For unevaluable expressions, the judgement is trivially held.
Preservation of typing under evaluation of K
is proven given:
- Evaluation of
K α β (x : α) y = x
- Evaluation of
(K α β x y).eval_once : α
(byx : α
and.eval_once = x
), then - Preservation of
K α β x y : α
is held
lemma k_def_eq : ∀ α β x y, (Call.mk SK[(((K α) β) x)] y).eval_once = x := by
intro α β x y
unfold Call.eval_once
repeat unfold NamedSkExpr.to_sk_expr
simp
lemma s_def_eq : ∀ α β γ x y z, (Call.mk SK[(((((S α) β) γ) x) y)] z).eval_once = SK[((x z)(y z))] := by
intro α β x y γ
unfold Call.eval_once
repeat unfold NamedSkExpr.to_sk_expr
simp
Assuming x : α
is the easy case, but does not prove preservation alone.
lemma k_x_judgement_holds_eval_once : ∀ α β x y, valid_judgement [] x α → valid_judgement [] (Call.mk SK[(((K α) β) x)] y).eval_once α := by
intro α β x y h_t_x
unfold Call.eval_once
repeat unfold NamedSkExpr.to_sk_expr
simp
exact h_t_x
Proving preservation in the general case of evaluation requires proving K α β x y : α → (K α β x y).eval_once : α
.
Substitution
This lemma becomes tricky once substitution is required. I prove that all substitutions of (var n) rhs
where n ≠ ctx.length
are no-ops. I also prove that all substitutions of (var 1) rhs = rhs
.
lemma substitute_free_noop : ∀ rhs v n, (Var.mk n) ≠ v → Fall.substitute.substitute_e (.var v) n rhs = (.var v) := by
intro rhs n h_n_gt_1
unfold Fall.substitute.substitute_e
simp
intro h
match h : SkExpr.var n with
| .var (Var.mk n') =>
simp_all
| .k _ => contradiction
| .s _ => contradiction
| .prp _ => contradiction
| .ty _ => contradiction
| .fall _ => contradiction
| .call _ => contradiction
lemma substitute_bound_1 : ∀ rhs, Fall.substitute.substitute_e (.var (.mk ⟨1⟩)) ⟨1⟩ rhs = rhs := by
intro rhs
unfold Fall.substitute.substitute_e
simp
lemma substitute_prp_noop : ∀ e rhs n, e = SkExpr.prp (.mk) → Fall.substitute.substitute_e e n rhs = e := by
intro e rhs n h_e_not_var
unfold Fall.substitute.substitute_e
rw [h_e_not_var]
lemma substitute_ty_noop : ∀ u e rhs n, e = SkExpr.ty (.mk u) → Fall.substitute.substitute_e e n rhs = e := by
intro u e rhs n h_e_not_var
unfold Fall.substitute.substitute_e
rw [h_e_not_var]
lemma substitute_k_noop : ∀ e rhs n, e = SkExpr.k (.mk) → Fall.substitute.substitute_e e n rhs = e := by
intro e rhs n h_e_not_var
unfold Fall.substitute.substitute_e
rw [h_e_not_var]
lemma substitute_s_noop : ∀ e rhs n, e = SkExpr.s (.mk) → Fall.substitute.substitute_e e n rhs = e := by
intro e rhs n h_e_not_var
unfold Fall.substitute.substitute_e
rw [h_e_not_var]
lemma n_eq_imp_bound_rhs : ∀ v n rhs, (.mk n) = v → Fall.substitute.substitute_e (.var v) n rhs = rhs := by
intro v n rhs h_v_n_eq
unfold Fall.substitute.substitute_e
match v with
| .mk n' =>
simp_all
Combinator evaluation type preservation
Now, I prove that valid_judgement SK[K α β x y] α → valid_judgement SK[K α β x y].eval_once α
by proving the call is well-typed with type α
.
Judgements of function application are determined to be true or false by substitution of the ∀
type of the left-hand side of the function call. This would seem to complicate the lemma once an argument to K
like ty_k
is provided. ty_k
is dependent, and contains variables. However, all arguments to K
are well-typed and closed. I demonstrate this later.
We can use this fact in our larger lemma.
However, the judgement rule allowing beta_eq t t' → valid_judgement e t → valid_judgement e t
further complicates matters. We defer to the later SN
proof with sorry
for most lemmas.
A variable is bound if ctx[(var n).n - 1] = some t
.
inductive is_bound : Ctx → SkExpr → Prop
| var ctx n : n.toNat > 0 → ⟨(n.toNat - 1)⟩ < (BindId.mk ctx.length) → is_bound ctx (.var (.mk n))
| app ctx c : is_bound ctx c.lhs → is_bound ctx c.rhs → is_bound ctx (.call c)
| fall ctx f : is_bound (f.bind_ty :: ctx) f.bind_ty → is_bound (f.bind_ty :: ctx) f.body → is_bound ctx (.fall f)
| k ctx k : is_bound ctx (.k k)
| s ctx s : is_bound ctx (.s s)
| prp ctx prp : is_bound ctx (.prp prp)
| ty ctx ty : is_bound ctx (.ty ty)
lemma all_well_typed_var_bound_iff (ctx : Ctx) : ∀ (n : BindId), (∃ t, valid_judgement ctx (.var (.mk n)) t) ↔ is_bound ctx (.var (.mk n)) := by
intro v
constructor
intro ⟨t_var, h_t_valid⟩
cases h_t_valid
case mp.beta_eq => sorry
case mp.var h_pos h_in_bounds =>
apply is_bound.var ctx
if h : v = { toNat := ctx.length } then
simp_all
else
simp_all
rw [LT.lt] at h_pos
simp [BindId.instLT] at h_pos
simp_all
rw [LT.lt]
simp [BindId.instLT]
exact h_in_bounds
case mpr =>
intro h_bound
cases h_bound
use ctx[v.toNat - 1]
apply valid_judgement.var
simp [GT.gt]
rw [LT.lt]
simp [BindId.instLT]
simp_all
I generalize this lemma to all expressions by induction on the definition of being bound.
lemma all_well_typed_e_bound_iff (ctx : Ctx) : ∀ e, (∃ t, valid_judgement ctx e t) → is_bound ctx e := by
intro e h_typed
cases e
case k =>
simp [is_bound.k]
case s =>
simp [is_bound.s]
case prp =>
simp [is_bound.prp]
case ty =>
simp [is_bound.ty]
case fall f =>
obtain ⟨t, h_valid_t⟩ := h_typed
match f with
| .mk bind_ty body =>
cases h_valid_t
case fall t_bind_ty h_bind_ty h_body =>
simp [Fall.bind_ty] at h_bind_ty
simp [Fall.body] at h_body
apply is_bound.fall
apply all_well_typed_e_bound_iff
use t_bind_ty
simp [Fall.bind_ty]
exact h_bind_ty
apply all_well_typed_e_bound_iff
use t
simp [Fall.body]
exact h_body
case beta_eq => sorry
case call c =>
obtain ⟨t, h_valid_t⟩ := h_typed
match c with
| .mk lhs rhs =>
cases h_valid_t
case call t_lhs h_t_lhs h_t_rhs =>
simp [Call.lhs] at h_t_lhs
simp [Call.rhs] at h_t_rhs
apply is_bound.app
apply all_well_typed_e_bound_iff
use (.fall t_lhs)
exact h_t_lhs
apply all_well_typed_e_bound_iff
use t_lhs.bind_ty
exact h_t_rhs
case beta_eq => sorry
case var v =>
obtain ⟨t, h_valid_t⟩ := h_typed
match v with
| .mk n =>
cases h_valid_t
case var h_pos h_bound =>
apply is_bound.var
simp [GT.gt] at h_pos
rw [LT.lt] at h_pos
simp [BindId.instLT] at h_pos
exact h_pos
rw [LT.lt]
simp [BindId.instLT]
exact h_bound
case beta_eq => sorry
lemma shift_indices_bound_noop : ∀ v_n shift_by (depth : ℕ), v_n.toNat ≤ depth → (SkExpr.var (.mk v_n)).with_indices_plus shift_by depth = (SkExpr.var (.mk v_n)) := by
intro v_n shift_by depth h_is_bound
simp [SkExpr.with_indices_plus]
simp_all
lemma all_e_well_typed_bound_shift_noop (ctx : Ctx) : ∀ (e : SkExpr) t shift_by, valid_judgement ctx e t → e.with_indices_plus shift_by ctx.length = e := by
intro e t shift_by h_judgement_t
match h : e with
| .k _ =>
simp [SkExpr.with_indices_plus]
| .s _ =>
simp [SkExpr.with_indices_plus]
| .prp _ =>
simp [SkExpr.with_indices_plus]
| .ty _ =>
simp [SkExpr.with_indices_plus]
| .fall (.mk bind_ty body) =>
unfold SkExpr.with_indices_plus
simp
cases h_judgement_t
case fall t_bind_ty h_t_bind_ty h_t =>
simp [Fall.bind_ty] at h_t_bind_ty
simp [Fall.body] at h_t
have h := all_e_well_typed_bound_shift_noop (bind_ty :: ctx) bind_ty t_bind_ty shift_by h_t_bind_ty
simp_all
have h := all_e_well_typed_bound_shift_noop (bind_ty :: ctx) body t shift_by (by simp [Fall.bind_ty] at h_t; exact h_t)
simp_all
case beta_eq => sorry
| .call (.mk lhs rhs) =>
unfold SkExpr.with_indices_plus
simp
cases h_judgement_t
case call t_lhs h_t_lhs h_t_rhs =>
simp [all_e_well_typed_bound_shift_noop ctx lhs (.fall t_lhs) shift_by h_t_lhs]
simp [all_e_well_typed_bound_shift_noop ctx rhs t_lhs.bind_ty shift_by h_t_rhs]
case beta_eq => sorry
| .var (.mk n) =>
have h₀ := h_judgement_t
cases h_judgement_t
case beta_eq => sorry
case var h_idx_valid =>
have h_bound := (all_well_typed_var_bound_iff ctx n).mp (by
use ctx[n.toNat - 1]
)
cases h_bound
case var h_pos h_bound =>
rw [LT.lt] at h_bound
simp [BindId.instLT] at h_bound
exact shift_indices_bound_noop n shift_by ctx.length (by rw [← Nat.pred_eq_sub_one] at h_bound; exact Nat.le_of_pred_lt h_bound)
Using the above lemma, we can prove that substitution of all bound variables is a noop.
lemma substitute_bound_noop (ctx : Ctx) : ∀ e with_expr, is_bound ctx e → Fall.substitute.substitute_e e ⟨ctx.length + 1⟩ with_expr = e := by
intro e with_expr h_bound
cases e
case k =>
simp [Fall.substitute.substitute_e]
case s =>
simp [Fall.substitute.substitute_e]
case prp =>
simp [Fall.substitute.substitute_e]
case ty =>
simp [Fall.substitute.substitute_e]
case fall f =>
match f with
| .mk bind_ty body =>
cases h_bound
case fall h_bind_ty_bound h_body_bound =>
simp [Fall.substitute.substitute_e]
constructor
exact substitute_bound_noop (bind_ty :: ctx) bind_ty with_expr h_bind_ty_bound
exact substitute_bound_noop (bind_ty :: ctx) body with_expr h_body_bound
case call c =>
match c with
| .mk lhs rhs =>
cases h_bound
case app h_bound_lhs h_bound_rhs =>
simp [Fall.substitute.substitute_e]
constructor
exact (substitute_bound_noop ctx lhs with_expr h_bound_lhs)
exact (substitute_bound_noop ctx rhs with_expr h_bound_rhs)
case var v =>
match h : v with
| .mk n =>
cases h_bound
case var h_bound =>
unfold Fall.substitute.substitute_e
simp
have h_bound' : (BindId.mk n.toNat) ≤ { toNat := ctx.length } := by
rw [LT.lt] at h_bound
simp [BindId.instLT] at h_bound
rw [LE.le]
simp [BindId.instLE]
omega
have h_bound'' : (BindId.mk n.toNat) < { toNat := ctx.length + 1 } := by
simp_all
rw [LT.lt]
simp [BindId.instLT]
rw [LE.le] at h_bound'
simp [BindId.instLE] at h_bound'
linarith
have h_bound''' : n < { toNat := ctx.length + 1 } := by
simp_all
simp_all
intro h
rw [h] at h_bound'''
exfalso
rw [LT.lt] at h_bound'''
simp [BindId.instLT] at h_bound'''
Using the fact that all variables that are well-typed are bound, we can say that with_indices_plus preserves the values of the variable. This concludes our lemma of preservation of K α β x y : α
.
Inconsistency
It should be noted that the judgement (K α β x y).eval_once : α
is obviously true, as evaluation of the K
combinator requires no substitution, and leaves α
unaltered. However, (K α β x y) : α
can potentially perform substitution, if α
contains free variables. However, K
and S
are closed and contain no free variables.
Closed expressions do not contain free variables, and as a result, are well-typed at their roots with the same type under any context.
TODO: Clean this lemma up a ton.
lemma judgement_holds_closed_any : ∀ ctx ctxxs e t, valid_judgement ctx e t → valid_judgement (ctx ++ ctxxs) e t := by
intro ctx ctxxs e t
intro h_closed
cases e
case k a =>
cases h_closed
case k =>
simp [valid_judgement.k]
case beta_eq => sorry
case s a =>
cases h_closed
case s =>
simp [valid_judgement.s]
case beta_eq => sorry
case prp a =>
cases h_closed
case prp =>
simp [valid_judgement.prp]
case beta_eq => sorry
case ty a =>
cases h_closed
case ty =>
simp [valid_judgement.ty]
case beta_eq => sorry
case fall f =>
match f with
| .mk bind_ty body =>
cases h_closed
case fall t_bind_ty h_t_bind_ty h_t_body =>
simp [Fall.body] at *
simp [Fall.bind_ty] at *
apply valid_judgement.fall (ctx ++ ctxxs) (.mk bind_ty body) t_bind_ty t
sorry
case beta_eq => sorry
case call c =>
match c with
| .mk lhs rhs =>
cases h_closed
case call t_lhs h_t_lhs h_t_rhs =>
apply valid_judgement.call (ctx ++ ctxxs) (.mk lhs rhs) t_lhs
simp [Call.lhs] at *
have h := (judgement_holds_closed_any ctx ctxxs lhs (SkExpr.fall t_lhs)).mp h_t_lhs
exact h
have h := (judgement_holds_closed_any ctx ctxxs rhs t_lhs.bind_ty).mp h_t_rhs
exact h
case beta_eq => sorry
case var _ =>
sorry
This is a massive lemma. We ought to modularize it.
lemma k_judgement_x_imp_judgement_call {m n : ℕ} : ∀ α β x y, valid_judgement [] α SK[Type m] → valid_judgement [] β SK[Type n] → valid_judgement [] x α → valid_judgement [] y β → valid_judgement [] SK[((((K α) β) x) y)] α := by
intro α β x y t_α t_β t_x t_y
simp [NamedSkExpr.to_sk_expr] at t_α
simp [NamedSkExpr.to_sk_expr] at t_β
have h : valid_judgement [] SK[((((K α) β) x) y)] ((Fall.mk β α).substitute y).body := (by
apply valid_judgement.call [] (Call.mk SK[(((K α) β) x)] y) (.mk β α)
simp [NamedSkExpr.to_sk_expr] at *
simp [Call.lhs]
have h : (valid_judgement [] (SkExpr.call (Call.mk (SkExpr.call (Call.mk (SkExpr.call (Call.mk (SkExpr.k «K».mk) α)) β)) x))
((Fall.mk α (SkExpr.fall (Fall.mk β α))).substitute
(Call.mk (SkExpr.call (Call.mk (SkExpr.call (Call.mk (SkExpr.k «K».mk) α)) β)) x).rhs).body) := (by
apply valid_judgement.call [] (Call.mk (SkExpr.call (Call.mk (SkExpr.call (Call.mk (SkExpr.k «K».mk) α)) β)) x) (.mk α (.fall (.mk β α)))
simp [Call.lhs]
have h := (valid_judgement.call [] (.mk SK[(K α)] β) (.mk SK[Type n] (.fall (.mk α (.fall (.mk (.var (.mk ⟨3⟩)) α))))) (by
simp [Call.lhs, NamedSkExpr.to_sk_expr]
have h : (valid_judgement [] SK[(K α)] (.fall (.mk SK[Type n] (.fall (.mk α (.fall (.mk (.var (.mk ⟨3⟩)) α))))))) := by
simp [NamedSkExpr.to_sk_expr]
have h := valid_judgement.call [] (.mk SK[K] α) (@ty_k_fall m n) (by
simp [Call.lhs]
rw [← ty_k_def_eq]
simp [NamedSkExpr.to_sk_expr, valid_judgement.k]
) (by
simp [Call.rhs]
simp [ty_k_fall, Fall.bind_ty]
exact t_α
)
simp [Fall.body, Call.rhs] at h
rw [ty_k_fall] at h
simp [NamedSkExpr.to_sk_expr, Fall.substitute, Fall.substitute.substitute_e, BindId.succ] at h
have h_shift_noop := all_e_well_typed_bound_shift_noop [] α SK[Type m] ⟨1⟩ (by
simp [NamedSkExpr.to_sk_expr]
exact t_α
)
simp [NamedSkExpr.to_sk_expr, List.length] at h_shift_noop
rw [h_shift_noop] at h
exact h
exact h
) (by
simp [Call.rhs, Fall.bind_ty]
exact t_β
))
simp [NamedSkExpr.to_sk_expr] at h
simp [Fall.substitute] at h
simp [Call.rhs] at h
simp [substitute_ty_noop] at h
simp [Fall.substitute.substitute_e, BindId.succ, Fall.body] at h
have h_α_sub_noop : Fall.substitute.substitute_e α { toNat := 3 } (β.with_indices_plus { toNat := 1 } 0) = α := by
have h_sub_noop := substitute_bound_noop [α, SK[Type m]] α (β.with_indices_plus { toNat := 1 } 0)
have h_bound : is_bound [α, SK[Type m]] α := by
apply all_well_typed_e_bound_iff
use SK[Type m]
exact (judgement_holds_closed_any List.nil [α, SK[Type m]] α SK[Type m]).mp t_α
simp [NamedSkExpr.to_sk_expr] at h_bound
simp [h_bound, NamedSkExpr.to_sk_expr] at h_sub_noop
exact h_sub_noop
simp [h_α_sub_noop] at h
have h_α_sub_noop : Fall.substitute.substitute_e α { toNat := 2 } (β.with_indices_plus { toNat := 1 } 0) = α := by
have h_sub_noop := substitute_bound_noop [SK[Type m]] α (β.with_indices_plus { toNat := 1 } 0)
have h_bound : is_bound [SK[Type m]] α := by
apply all_well_typed_e_bound_iff
use SK[Type m]
exact (judgement_holds_closed_any List.nil [SK[Type m]] α SK[Type m]).mp t_α
simp [NamedSkExpr.to_sk_expr] at h_bound
simp [h_bound, NamedSkExpr.to_sk_expr] at h_sub_noop
exact h_sub_noop
simp [h_α_sub_noop] at h
have h_shift_noop := all_e_well_typed_bound_shift_noop [] β SK[Type n] ⟨1⟩ (by
simp [NamedSkExpr.to_sk_expr]
exact t_β
)
simp at h_shift_noop
simp [h_shift_noop] at h
exact h
simp [Call.rhs]
simp [Fall.bind_ty]
exact t_x
)
simp [Fall.substitute, Fall.substitute.substitute_e, Call.rhs, BindId.succ, Fall.body] at h
have h_sub_noop_h := substitute_bound_noop [α] β (x.with_indices_plus { toNat := 1 } 0) (by
apply all_well_typed_e_bound_iff
use SK[Type n]
exact (judgement_holds_closed_any List.nil [α] β SK[Type n]).mp t_β)
simp at h_sub_noop_h
rw [h_sub_noop_h] at h
have h_sub_noop_h := substitute_bound_noop [SK[Type m]] α (x.with_indices_plus { toNat := 1 } 0) (by
apply all_well_typed_e_bound_iff
use SK[Type m]
exact (judgement_holds_closed_any List.nil [SK[Type m]] α SK[Type m]).mp t_α)
simp at h_sub_noop_h
rw [h_sub_noop_h] at h
exact h
simp [Fall.bind_ty]
simp [NamedSkExpr.to_sk_expr, Call.rhs]
exact t_y
)
simp [NamedSkExpr.to_sk_expr] at *
simp [Fall.substitute] at h
simp [Fall.body] at h
have h_sub_alpha_noop := substitute_bound_noop [] α (y.with_indices_plus { toNat := 1 } 0) (by
cases α
case k =>
simp [is_bound.k]
case s =>
simp [is_bound.s]
case prp =>
simp [is_bound.prp]
case ty =>
simp [is_bound.ty]
case fall f =>
match f with
| .mk bind_ty body =>
cases t_α
case fall t_bind_ty h_t_bind_ty h_t_body =>
apply is_bound.fall
simp [Fall.bind_ty]
apply all_well_typed_e_bound_iff
use t_bind_ty
simp [Fall.bind_ty] at h_t_bind_ty
exact h_t_bind_ty
apply all_well_typed_e_bound_iff
use (SkExpr.ty (Ty.mk m))
case beta_eq =>
sorry
case call c =>
match h_c : c with
| .mk lhs rhs =>
-- call lhs rhs is surely of type Ty n
-- this implies lhs's type is of the form ∀
apply all_well_typed_e_bound_iff
use (.ty (Ty.mk m))
case var v =>
match v with
| .mk n =>
apply (all_well_typed_var_bound_iff [] n).mp
use (.ty (Ty.mk m))
)
simp at h_sub_alpha_noop
simp [h_sub_alpha_noop] at h
exact h
I do the same for \(S\).
lemma judgement_holds_eval_once (ctx : Ctx) : ∀ (call : Call) (t : SkExpr), valid_judgement ctx (.call call) t ↔ valid_judgement ctx call.eval_once t := by
intro c t
unfold Call.eval_once
split
case h_1 α β x y =>
constructor
intro h
cases h
case mp.call =>
sorry
lemma eval_once_candidate_iff (ctx : Ctx) : ∀ (call : Call) (t : SkExpr), is_candidate_for_t ctx (.call call) t ↔ is_candidate_for_t ctx (call.eval_once) t := by
intro e t
constructor
case mp =>
intro h
match h with
| .fn e' t' t_judgement arg h_t_arg h_redux => sorry
| .ty e' t' ht' =>
have h_judgement_t := candidate_for_t_imp_judgement_t ctx (.call e) t h
-- Since
sorry
| .prp _ _ _ => sorry
| .fall _ _ _ => sorry
case mpr =>
intro h
sorry
Made by Dowland Aiello.