-- https://plfa.github.io/BigStep/ import Plfl.Init import Plfl.Untyped import Plfl.Untyped.Substitution namespace BigStep open Untyped (Context) open Untyped.Notation open Substitution (Subst ids sub_ids) -- https://plfa.github.io/BigStep/#environments /-- A closure in call-by-name is a term paired with its environment. -/ inductive Clos : Type where | clos : ∀ {Γ}, (m : Γ ⊢ ✶) → (γ : (Γ ∋ ✶) → Clos) → Clos /-- An environment in call-by-name is a mapping from variables to closures. -/ abbrev ClosEnv (Γ : Context) := (Γ ∋ ✶) → Clos def ClosEnv.empty : ClosEnv ∅ := by intro. instance ClosEnv.instEmptyCollection : EmptyCollection (ClosEnv ∅) where emptyCollection := empty def ClosEnv.tail (γ : ClosEnv Γ) (c : Clos) : ClosEnv (Γ‚ ✶) | .z => c | .s i => γ i namespace Notation -- `‚` is not a comma! See: scoped infixl:50 "‚' " => ClosEnv.tail end Notation open Notation inductive Eval : ClosEnv Γ → (Γ ⊢ ✶) → Clos → Prop where | var : γ i = .clos m δ → Eval δ m v → Eval γ (` i) v | lam : Eval γ (ƛ m) (.clos (ƛ m) γ) | ap : Eval γ l (.clos (ƛ n) δ) → Eval (δ‚' .clos m γ) n v → Eval γ (l □ m) v namespace Notation scoped notation:40 γ " ⊢ " m " ⇓ " c:51 => Eval γ m c end Notation -- https://plfa.github.io/BigStep/#exercise-big-step-eg-practice example : γ ⊢ (ƛ ƛ #1) $ (ƛ #0 □ #0) $ (ƛ #0 □ #0) -- (λ x y => x) ((λ f => f f) (λ f => f f)) ⇓ (λ y => ((λ f => f f) (λ f => f f))) ⇓ .clos (ƛ #1) (γ‚' .clos ((ƛ #0 □ #0) $ (ƛ #0 □ #0)) γ) := .ap .lam .lam -- https://plfa.github.io/BigStep/#the-big-step-semantics-is-deterministic theorem Eval.determ (e : γ ⊢ m ⇓ v) (e' : γ ⊢ m ⇓ v') : v = v' := by induction e generalizing v' with cases e' | lam => rfl | var h _ ih => subst_vars; rename_i h' e'; injection h.symm.trans h' rename_i h hh hh'; subst h; rw [←hh.eq, ←hh'.eq] at e'; exact ih e' | ap _ _ ih ih₁ => rename_i e' e₁'; apply ih₁; injection ih e' subst_vars; rename_i h; injection h; subst_vars; exact e₁' -- https://plfa.github.io/BigStep/#big-step-evaluation-implies-beta-reduction-to-a-lambda noncomputable def Clos.Equiv : Clos → (∅ ⊢ ✶) → Prop | .clos (Γ := Γ) m γ, n => ∃ (σ : Subst Γ ∅), (∀ i, Clos.Equiv (γ i) (σ i)) ∧ (n = ⟪σ⟫ m) abbrev ClosEnv.Equiv (γ : ClosEnv Γ) (σ : Subst Γ ∅) : Prop := ∀ i, Clos.Equiv (γ i) (σ i) namespace Notation -- The default precedence in Agda is 20. -- See: scoped infix:20 " ~~ " => Clos.Equiv scoped infix:20 " ~~ₑ " => ClosEnv.Equiv end Notation section open Untyped.Subst open Substitution @[simp] lemma ClosEnv.empty_equiv_ids : ∅ ~~ₑ ids := by intro. abbrev ext_subst (σ : Subst Γ Δ) (n : Δ ⊢ ✶) : Subst (Γ‚ ✶) Δ := (·⟦n⟧) ∘ exts σ lemma subst₁σ_exts {σ : Subst Γ Δ} {m : Δ ⊢ b} {i : Γ ∋ ✶} : (ext_subst σ m) (.s i) = σ i := by simp only [subst₁σ_exts_cons] theorem ClosEnv.ext {γ : ClosEnv Γ} {σ : Subst Γ ∅} {n : ∅ ⊢ ✶} (ee : γ ~~ₑ σ) (e : v ~~ n) : (γ‚' v ~~ₑ ext_subst σ n) := by intro | .z => exact e | .s i => simp only [subst₁σ_exts]; exact ee i theorem Eval.clos_env_equiv {γ : ClosEnv Γ} {σ : Subst Γ ∅} {m : Γ ⊢ ✶} (ev : γ ⊢ m ⇓ v) (ee : γ ~~ₑ σ) : ∃ (n : ∅ ⊢ ✶), (⟪σ⟫ m —↠ n) ∧ (v ~~ n) := open Untyped.Reduce in by induction ev with | lam => rename_i n; exists ⟪σ⟫ (ƛ n), by rfl, σ, ee | var h _ev ih => rename_i i; have := ee i; rw [h] at this; have ⟨τ, eeτ, hτ⟩ := this have ⟨n, rn, en⟩ := ih eeτ; rw [←hτ] at rn; exists n, rn | ap _ev _ev' ih ih' => have ⟨n, rn, τ, eeτ, hτ⟩ := ih ee; subst hτ have ⟨n', rn', en'⟩ := ih' <| ClosEnv.ext eeτ ⟨σ, ee, rfl⟩ refine ⟨n', ?_, en'⟩; simp only [sub_ap]; rename_i n _ m _ apply (ap_congr₁ rn).trans; unfold ext_subst at rn' calc ⟪τ⟫ (ƛ n) □ ⟪σ⟫ m _ = (ƛ (⟪exts τ⟫ n)) □ ⟪σ⟫ m := rfl _ —→ ⟪subst₁σ (⟪σ⟫ m)⟫ (⟪exts τ⟫ n) := lamβ _ = ⟪⟪subst₁σ (⟪σ⟫ m)⟫ ∘ exts τ⟫ n := Substitution.sub_sub _ —↠ n' := rn' /-- If call-by-name can produce a value, then the program can be reduced to a λ-abstraction via β-rules. -/ theorem Eval.reduce_of_cbn {m : ∅ ⊢ ✶} {δ : ClosEnv Δ} {n' : Δ‚ ✶ ⊢ ✶} (ev : ∅ ⊢ m ⇓ .clos (ƛ n') δ) : ∃ (n : ∅‚ ✶ ⊢ ✶), m —↠ ƛ n := by have ⟨n, rn, σ, _, h⟩ := ev.clos_env_equiv ClosEnv.empty_equiv_ids subst h; rw [sub_ids] at rn; exists ⟪exts σ⟫ n' end -- https://plfa.github.io/BigStep/#exercise-big-alt-implies-multi-practice namespace BySubst -- https://github.com/L-TChen/ModalTypeTheory/blob/a4d3cf67236716fa324daa3e5a929f38a33c39e9/src/STLC/BigStep.agda#L96-L121 -- https://www.cs.cornell.edu/courses/cs6110/2014sp/Handouts/Sestoft.pdf inductive Eval : (Γ ⊢ ✶) → (Γ ⊢ ✶) → Prop where -- Hmmm, it's all ƛ's after all? | lam : ∀ {n : ∅‚ ✶ ⊢ ✶}, Eval (ƛ n) (ƛ n) | ap : Eval l (ƛ m) → Eval (m⟦n⟧) v → Eval (l □ n) v namespace Notation scoped infix:50 " ⇓' "=> Eval end Notation open Notation theorem Eval.determ : m ⇓' v → m ⇓' v' → v = v' := by intro | .lam, .lam => rfl | .ap mc mc₁, .ap mc' mc₁' => have := mc.determ mc'; injection this; subst_vars; exact mc₁.determ mc₁' open Untyped.Reduce open Untyped.Subst /-- If call-by-name can produce a value, then the program can be reduced to a λ-abstraction via β-rules. -/ theorem Eval.reduce_of_cbn {n : Γ‚ ✶ ⊢ ✶} (ev : m ⇓' (ƛ n)) : m —↠ ƛ n := by generalize hx : (ƛ n) = x, hx' : m = x' at * induction ev with | lam => rfl | ap _evl evmn' ih ih' => subst_vars; rename_i l m n'; calc l □ n' _ —↠ (ƛ m) □ n' := ap_congr₁ <| ih rfl rfl _ —→ m⟦n'⟧ := lamβ _ —↠ (ƛ n) := ih' rfl rfl