-- https://plfa.github.io/Compositional/ import Plfl.Init import Plfl.Untyped.Denotational namespace Compositional open Untyped.Notation open Denotational Denotational.Notation -- https://plfa.github.io/Compositional/#equation-for-lambda-abstraction def ℱ (d : Denot (Γ‚ ✶)) : Denot Γ | _, ⊥ => ⊤ | γ, v ⇾ w => d (γ`‚ v) w | γ, .conj u v => ℱ d γ u ∧ ℱ d γ v lemma sub_ℱ (d : ℱ (ℰ n) γ v) (lt : u ⊑ v) : ℱ (ℰ n) γ u := by induction lt with | bot => trivial | conjL _ _ ih ih' => exact ⟨ih d, ih' d⟩ | conjR₁ _ ih => exact ih d.1 | conjR₂ _ ih => exact ih d.2 | trans _ _ ih ih' => exact ih (ih' d); | fn lt lt' => exact .sub (up_env d lt) lt' | dist => exact .conj d.1 d.2 lemma ℱ_ℰ (d : ℰ (ƛ n) γ v) : ℱ (ℰ n) γ v := by generalize hx : (ƛ n) = x at * induction d with try injection hx | fn d => subst_vars; exact d | bot => trivial | conj _ _ ih ih' => exact ⟨ih hx, ih' hx⟩ | sub _ lt ih => exact sub_ℱ (ih hx) lt theorem lam_inv (d : γ ⊢ ƛ n ↓ v ⇾ v') : (γ`‚ v) ⊢ n ↓ v' := ℱ_ℰ d lemma ℰ_lam (d : ℱ (ℰ n) γ v) : ℰ (ƛ n) γ v := match v with | .bot => .bot | .fn _ _ => .fn d | .conj _ _ => (ℰ_lam d.1).conj (ℰ_lam d.2) theorem lam_equiv : ℰ (ƛ n) = ℱ (ℰ n) := by ext; exact ⟨ℱ_ℰ, ℰ_lam⟩ -- https://plfa.github.io/Compositional/#equation-for-function-application abbrev 𝒜 (d d' : Denot Γ) : Denot Γ | γ, w => (w ⊑ ⊥) ∨ (∃ v, d γ (v ⇾ w) ∧ d' γ v) namespace Notation scoped infixl:70 " ● " => 𝒜 end Notation open Notation lemma 𝒜_ℰ (d : ℰ (l □ m) γ v) : (ℰ l ● ℰ m) γ v := by generalize hx : l □ m = x at * induction d with try injection hx | bot => left; rfl | ap d d' => subst_vars; right; rename_i v' _ _ _ _; exists v' | sub _ lt ih => match ih hx with | .inl lt' => left; exact lt.trans lt' | .inr ⟨v', efv', ev'⟩ => right; refine ⟨v', efv'.sub ?_, ev'⟩; exact .fn .refl lt | conj _ _ ih ih' => match ih hx, ih' hx with | .inl lt, .inl lt' => left; exact lt.conjL lt' | .inl lt, .inr ⟨v', efv', ev'⟩ => right; refine ⟨v', efv'.sub ?_, ev'⟩; refine .fn .refl ?_ refine .conjL ?_ .refl; exact sub_of_sub_bot lt | .inr ⟨v', efv', ev'⟩, .inl lt => right; refine ⟨v', efv'.sub ?_, ev'⟩; refine .fn .refl ?_ refine .conjL .refl ?_; exact sub_of_sub_bot lt | .inr ⟨v', efv', ev'⟩, .inr ⟨v'', efv'', ev''⟩ => right; refine ⟨v' ⊔ v'', ?_, ev'.conj ev''⟩ exact (efv'.conj efv'').sub fn_conj_sub_conj_fn lemma ℰ_ap : (ℰ l ● ℰ m) γ v → ℰ (l □ m) γ v | .inl lt => .sub .bot lt | .inr ⟨_, efv, ev⟩ => efv.ap ev theorem ap_equiv : ℰ (l □ m) = (ℰ l ● ℰ m) := by ext; exact ⟨𝒜_ℰ, ℰ_ap⟩ abbrev 𝒱 (i : Γ ∋ ✶) (γ : Env Γ) (v : Value) : Prop := v ⊑ γ i theorem var_inv (d : ℰ (` i) γ v) : 𝒱 i γ v := by generalize hx : (` i) = x at * induction d with try injection hx | var => subst_vars; rfl | bot => exact .bot | conj _ _ ih ih' => exact (ih hx).conjL (ih' hx) | sub _ lt ih => exact lt.trans (ih hx) theorem var_equiv : ℰ (` i) = 𝒱 i := by ext; exact ⟨var_inv, .sub .var⟩ -- https://plfa.github.io/Compositional/#congruence lemma lam_congr (h : ℰ n = ℰ n') : ℰ (ƛ n) = ℰ (ƛ n') := calc _ _ = ℱ (ℰ n) := lam_equiv _ = ℱ (ℰ n') := by rw [h] _ = ℰ (ƛ n') := lam_equiv.symm lemma ap_congr (hl : ℰ l = ℰ l') (hm : ℰ m = ℰ m') : ℰ (l □ m) = ℰ (l' □ m') := calc _ _ = ℰ l ● ℰ m := ap_equiv _ = ℰ l' ● ℰ m' := by rw [hl, hm] _ = ℰ (l' □ m') := ap_equiv.symm -- https://plfa.github.io/Compositional/#compositionality open Untyped (Context) /-- `Holed Γ Δ` describes a program with a hole in it: - `Γ` is the `Context` for the hole. - `Δ` is the `Context` for the terms that result from filling the hole. -/ inductive Holed : Context → Context → Type where /-- A basic hole. -/ | hole : Holed Γ Γ /-- λ-abstracting the hole makes a bigger hole. -/ | lam : Holed (Γ‚ ✶) (Δ‚ ✶) → Holed (Γ‚ ✶) Δ /-- Applying to a holed function makes a bigger hole. -/ | apL : Holed Γ Δ → (Δ ⊢ ✶) → Holed Γ Δ /-- Applying a holed argument makes a bigger hole. -/ | apR : (Δ ⊢ ✶) → Holed Γ Δ → Holed Γ Δ /-- `plug`s a term into a `Holed` context, making a new term. -/ def Holed.plug : Holed Γ Δ → (Γ ⊢ ✶) → (Δ ⊢ ✶) | .hole, m => m | .lam c, n => ƛ c.plug n | .apL c n, l => c.plug l □ n | .apR l c, m => l □ c.plug m /-- Given two terms that are denotationally equal, plugging them both into any holed context produces two programs that are denotationally equal. -/ theorem compositionality {c : Holed Γ Δ} (h : ℰ m = ℰ n) : ℰ (c.plug m) = ℰ (c.plug n) := by induction c with unfold Holed.plug | hole => exact h | lam _ ih => exact lam_congr (ih h) | apL _ _ ih => exact ap_congr (ih h) (by rfl) | apR _ _ ih => exact ap_congr (by rfl) (ih h) -- https://plfa.github.io/Compositional/#the-denotational-semantics-defined-as-a-function /-- `ℰ₀ m` is the instance of `Denot` that corresponds to the `Eval` of `m`. It is like `ℰ m`, but defined computationally. -/ def ℰ₀ : (Γ ⊢ ✶) → Denot Γ | ` i => 𝒱 i | ƛ n => ℱ (ℰ₀ n) | l □ m => ℰ₀ l ● ℰ₀ m /-- The two definitions of `ℰ` are equivalent. -/ theorem ℰ_eq_ℰ₀ : ℰ (Γ := Γ) = ℰ₀ := by ext; rw [impl] where impl {a} {m : Γ ⊢ a} : ℰ m = ℰ₀ m := by induction m with (ext γ v; simp only [ℰ₀]) | var i => rw [var_equiv] | lam n ih => rw [←ih, lam_equiv] | ap l m ih ih' => rw [←ih, ←ih', ap_equiv]