33--
44-- Models the core type system from docs/spec/FORMAL-SEMANTICS.md:
55-- - Syntax: Expr inductive with Num, Str, Bool, Identity, BraidLit,
6- -- Compose (.), Tensor (|), Pipeline (>>), Close, Add, Eq, and the echo
7- -- constructors EchoClose, Lower, Residue, EchoVal (structured loss)
6+ -- Compose (.), Tensor (|), Pipeline (>>), Close, Add, Eq, the echo
7+ -- constructors EchoClose, Lower, Residue, EchoVal (structured loss), and
8+ -- the product constructors Pair, Fst, Snd, EchoAdd
89-- - Typing: HasType inductive relation covering T-Num, T-Str, T-Bool,
910-- T-Identity, T-Braid, T-Compose-Word, T-Tensor-Word, T-Pipeline,
10- -- T-Close-Word, T-Add-Num, T-Eq-Word, T-Eq-Num, T-Eq-Str, and the echo
11- -- rules T-Echo-Close, T-Lower, T-Residue, T-Echo-Val
12- -- - Semantics: Small-step Step relation (38 rules incl. echo)
11+ -- T-Close-Word, T-Add-Num, T-Eq-Word, T-Eq-Num, T-Eq-Str, the echo
12+ -- rules T-Echo-Close, T-Lower, T-Residue, T-Echo-Val, and the product
13+ -- rules T-Pair, T-Fst, T-Snd, T-Echo-Add
14+ -- - Semantics: Small-step Step relation (47 rules incl. echo + product)
1315--
1416-- Theorems proven:
1517-- 1. Progress: well-typed closed terms are values or can step
2325-- constructors `echoClose`/`lower`/`residue` integrate echo-types
2426-- (hyperpolymath/echo-types: `Echo f y := Σ (x : A), f x ≡ y`) directly into
2527-- the type system: closing a braid through an echo retains the residue, so the
26- -- otherwise-irreversible `close` becomes reversible at the type level. See the
28+ -- otherwise-irreversible `close` becomes reversible at the type level. The
29+ -- product type `Ty.prod ρ σ` carries a second lossy operation, `echoAdd`:
30+ -- ordinary `add` discards which two numbers were summed, but `echoAdd` keeps
31+ -- the summand pair as its residue (residue type `Num × Num`, result `Num`), so
32+ -- distinct summands that collapse to the same sum stay distinguishable. See the
2733-- §ECHO-TYPES section at the foot of the file for the residue-recovery and
28- -- non-injectivity theorems.
34+ -- non-injectivity theorems (both the `close` and `echoAdd` forms) .
2935--
3036-- Note on T-Let: the let binding requires a generalized de Bruijn substitution
3137-- lemma (standard POPLmark machinery); tracked as TG-1. The fragment here
@@ -62,6 +68,7 @@ inductive Ty where
6268 -- (hyperpolymath/echo-types, Echo.agda): ρ is the
6369 -- residue (domain witness x : A), τ is the result
6470 -- (codomain point y). See §ECHO-TYPES below.
71+ | prod : Ty → Ty → Ty -- product (pair) type ρ × σ; residue carrier for lossy binary ops
6572 deriving DecidableEq, Repr
6673
6774/-- Core expression AST. Mirrors the OCaml AST in compiler/lib/ast.ml.
@@ -88,6 +95,10 @@ inductive Expr where
8895 | lower : Expr → Expr -- project an echo to its result (forget residue)
8996 | residue : Expr → Expr -- project an echo to its residue (recover witness)
9097 | echoVal : Expr → Expr → Expr -- formed echo value: (residue, result)
98+ | pair : Expr → Expr → Expr -- product introduction
99+ | fst : Expr → Expr -- first projection
100+ | snd : Expr → Expr -- second projection
101+ | echoAdd : Expr → Expr → Expr -- echo-preserving addition (residue = pair of summands)
91102 deriving DecidableEq, Repr
92103
93104/-- Value predicate: fully reduced expressions. -/
@@ -98,6 +109,7 @@ inductive IsValue : Expr → Prop where
98109 | identity : IsValue .identity
99110 | braidLit : ∀ gs, IsValue (.braidLit gs)
100111 | echoVal : ∀ {r v}, IsValue r → IsValue v → IsValue (.echoVal r v) -- a formed echo value (residue r, result v)
112+ | pair : ∀ {a b}, IsValue a → IsValue b → IsValue (.pair a b)
101113
102114-- ═══════════════════════════════════════════════════════════════════════
103115-- WIDTH
@@ -176,6 +188,15 @@ inductive HasType : Ctx → Expr → Ty → Prop where
176188 HasType Γ r ρ →
177189 HasType Γ v τ →
178190 HasType Γ (.echoVal r v) (.echo ρ τ)
191+ | tPair (Γ : Ctx) (a b : Expr) (α β : Ty) : -- [ T-Pair ]
192+ HasType Γ a α → HasType Γ b β → HasType Γ (.pair a b) (.prod α β)
193+ | tFst (Γ : Ctx) (e : Expr) (α β : Ty) : -- [ T-Fst ]
194+ HasType Γ e (.prod α β) → HasType Γ (.fst e) α
195+ | tSnd (Γ : Ctx) (e : Expr) (α β : Ty) : -- [ T-Snd ]
196+ HasType Γ e (.prod α β) → HasType Γ (.snd e) β
197+ | tEchoAdd (Γ : Ctx) (e₁ e₂ : Expr) : -- [ T-Echo-Add ]
198+ HasType Γ e₁ .num → HasType Γ e₂ .num →
199+ HasType Γ (.echoAdd e₁ e₂) (.echo (.prod .num .num) .num)
179200
180201-- ═══════════════════════════════════════════════════════════════════════
181202-- SMALL-STEP SEMANTICS
@@ -234,6 +255,18 @@ inductive Step : Expr → Expr → Prop where
234255 | lowerVal : IsValue r → IsValue v → Step (.lower (.echoVal r v)) v
235256 | residueStep : Step e e' → Step (.residue e) (.residue e')
236257 | residueVal : IsValue r → IsValue v → Step (.residue (.echoVal r v)) r
258+ -- Product: congruence + projections
259+ | pairLeft : Step a a' → Step (.pair a b) (.pair a' b)
260+ | pairRight : IsValue a → Step b b' → Step (.pair a b) (.pair a b')
261+ | fstStep : Step e e' → Step (.fst e) (.fst e')
262+ | fstPair : IsValue a → IsValue b → Step (.fst (.pair a b)) a
263+ | sndStep : Step e e' → Step (.snd e) (.snd e')
264+ | sndPair : IsValue a → IsValue b → Step (.snd (.pair a b)) b
265+ -- Echo-preserving addition: residue retains the summand pair; result is the sum.
266+ | echoAddLeft : Step e₁ e₁' → Step (.echoAdd e₁ e₂) (.echoAdd e₁' e₂)
267+ | echoAddRight : IsValue e₁ → Step e₂ e₂' → Step (.echoAdd e₁ e₂) (.echoAdd e₁ e₂')
268+ | echoAddNums : Step (.echoAdd (.num n₁) (.num n₂))
269+ (.echoVal (.pair (.num n₁) (.num n₂)) (.num (n₁ + n₂)))
237270
238271-- ═══════════════════════════════════════════════════════════════════════
239272-- LEMMAS
@@ -246,6 +279,9 @@ theorem value_no_step {e e' : Expr} (hv : IsValue e) (hs : Step e e') : False :=
246279 | echoVal _ _ ihr ihv => cases hs with
247280 | echoValLeft h => exact ihr h
248281 | echoValRight _ h => exact ihv h
282+ | pair _ _ iha ihb => cases hs with
283+ | pairLeft h => exact iha h
284+ | pairRight _ h => exact ihb h
249285 | _ => cases hs
250286
251287/-- Canonical forms for Num. -/
@@ -267,6 +303,7 @@ theorem canonical_word : IsValue e → HasType [] e (.word n) →
267303 | identity => left; cases ht with | tIdentity => exact ⟨rfl, rfl⟩
268304 | braidLit gs => right; cases ht with | tBraid => exact ⟨gs, rfl, rfl⟩
269305 | echoVal _ _ => cases ht
306+ | pair _ _ => cases ht
270307
271308/-- Canonical forms for Echo[ρ, τ]: a value of echo type is a formed echo value
272309 `echoVal r v` whose residue `r` and result `v` are themselves values. This
@@ -281,6 +318,22 @@ theorem canonical_echo : IsValue e → HasType [] e (.echo ρ τ) →
281318 | identity => cases ht
282319 | braidLit => cases ht
283320 | echoVal hr hv => exact ⟨_, _, rfl, hr, hv⟩
321+ | pair _ _ => cases ht
322+
323+ /-- Canonical forms for products: a value of product type is a `pair a b` whose
324+ components `a` and `b` are themselves values. This is the canonical form
325+ that lets `fst`/`snd` make progress. -/
326+ theorem canonical_prod : IsValue e → HasType [] e (.prod α β) →
327+ ∃ a b, e = .pair a b ∧ IsValue a ∧ IsValue b := by
328+ intro hv ht
329+ cases hv with
330+ | num => cases ht
331+ | str => cases ht
332+ | boolLit => cases ht
333+ | identity => cases ht
334+ | braidLit => cases ht
335+ | echoVal _ _ => cases ht
336+ | pair ha hb => exact ⟨_, _, rfl, ha, hb⟩
284337
285338-- Width distribution lemmas
286339private theorem foldl_max_init (gs : List Generator) (a : Nat) :
@@ -431,6 +484,33 @@ theorem progress : HasType [] e τ → IsValue e ∨ ∃ e', Step e e' := by
431484 · obtain ⟨r, v, rfl, hr, hvv⟩ := canonical_echo hv h
432485 exact ⟨_, .residueVal hr hvv⟩
433486 · exact ⟨_, .residueStep hs⟩
487+ | tPair _ _ α β ha hb =>
488+ rcases progress ha with hva | ⟨a', hsa⟩
489+ · rcases progress hb with hvb | ⟨b', hsb⟩
490+ · exact .inl (.pair hva hvb)
491+ · exact .inr ⟨_, .pairRight hva hsb⟩
492+ · exact .inr ⟨_, .pairLeft hsa⟩
493+ | tFst _ α β h =>
494+ right
495+ rcases progress h with hv | ⟨e', hs⟩
496+ · obtain ⟨a, b, rfl, ha, hb⟩ := canonical_prod hv h
497+ exact ⟨_, .fstPair ha hb⟩
498+ · exact ⟨_, .fstStep hs⟩
499+ | tSnd _ α β h =>
500+ right
501+ rcases progress h with hv | ⟨e', hs⟩
502+ · obtain ⟨a, b, rfl, ha, hb⟩ := canonical_prod hv h
503+ exact ⟨_, .sndPair ha hb⟩
504+ · exact ⟨_, .sndStep hs⟩
505+ | tEchoAdd _ _ h₁ h₂ =>
506+ right
507+ rcases progress h₁ with hv₁ | ⟨e₁', hs₁⟩
508+ · rcases progress h₂ with hv₂ | ⟨e₂', hs₂⟩
509+ · obtain ⟨n₁, rfl⟩ := canonical_num hv₁ h₁
510+ obtain ⟨n₂, rfl⟩ := canonical_num hv₂ h₂
511+ exact ⟨_, .echoAddNums⟩
512+ · exact ⟨_, .echoAddRight hv₁ hs₂⟩
513+ · exact ⟨_, .echoAddLeft hs₁⟩
434514
435515-- ═══════════════════════════════════════════════════════════════════════
436516-- THEOREM 2: PRESERVATION
@@ -550,6 +630,20 @@ theorem preservation : HasType [] e τ → Step e e' → HasType [] e' τ := by
550630 | residueVal _ _ =>
551631 cases ht with | tResidue _ _ _ h =>
552632 cases h with | tEchoVal _ _ _ _ hr hv => exact hr
633+ -- Product: congruence preserves the product type; projections recover the
634+ -- component types. `echoAdd` reduces into a formed echo value whose residue
635+ -- is the (num, num) summand pair and whose result is the num sum.
636+ | pairLeft hs ih => cases ht with | tPair _ _ α β ha hb => exact .tPair _ _ _ _ _ (ih ha) hb
637+ | pairRight _ hs ih => cases ht with | tPair _ _ α β ha hb => exact .tPair _ _ _ _ _ ha (ih hb)
638+ | fstStep hs ih => cases ht with | tFst _ α β h => exact .tFst _ _ _ _ (ih h)
639+ | fstPair _ _ => cases ht with | tFst _ α β h => cases h with | tPair _ _ _ _ ha hb => exact ha
640+ | sndStep hs ih => cases ht with | tSnd _ α β h => exact .tSnd _ _ _ _ (ih h)
641+ | sndPair _ _ => cases ht with | tSnd _ α β h => cases h with | tPair _ _ _ _ ha hb => exact hb
642+ | echoAddLeft hs ih => cases ht with | tEchoAdd _ _ h₁ h₂ => exact .tEchoAdd _ _ _ (ih h₁) h₂
643+ | echoAddRight _ hs ih => cases ht with | tEchoAdd _ _ h₁ h₂ => exact .tEchoAdd _ _ _ h₁ (ih h₂)
644+ | echoAddNums =>
645+ cases ht with | tEchoAdd _ _ h₁ h₂ =>
646+ exact .tEchoVal _ _ _ _ _ (.tPair _ _ _ _ _ (.tNum _ _) (.tNum _ _)) (.tNum _ _)
553647
554648-- ═══════════════════════════════════════════════════════════════════════
555649-- THEOREM 3: DETERMINISM
@@ -714,6 +808,39 @@ theorem determinism : Step e e₁ → Step e e₂ → e₁ = e₂ := by
714808 | residueVal hr hv => cases hs₂ with
715809 | residueStep h => exact absurd h (value_no_step (.echoVal hr hv))
716810 | residueVal _ _ => rfl
811+ -- Product: congruence is deterministic by IH; projections fire only on a
812+ -- formed pair value, so they never race their congruence rule. `echoAdd`
813+ -- computes only on two `num` values.
814+ | pairLeft hs ih => cases hs₂ with
815+ | pairLeft h => exact congrArg (Expr.pair · _) (ih h)
816+ | pairRight hva _ => exact absurd hs (value_no_step hva)
817+ | pairRight hva hs ih => cases hs₂ with
818+ | pairLeft h => exact absurd h (value_no_step hva)
819+ | pairRight _ h => exact congrArg (Expr.pair _ ·) (ih h)
820+ | fstStep hs ih => cases hs₂ with
821+ | fstStep h => exact congrArg Expr.fst (ih h)
822+ | fstPair ha hb => exact absurd hs (value_no_step (.pair ha hb))
823+ | fstPair ha hb => cases hs₂ with
824+ | fstStep h => exact absurd h (value_no_step (.pair ha hb))
825+ | fstPair _ _ => rfl
826+ | sndStep hs ih => cases hs₂ with
827+ | sndStep h => exact congrArg Expr.snd (ih h)
828+ | sndPair ha hb => exact absurd hs (value_no_step (.pair ha hb))
829+ | sndPair ha hb => cases hs₂ with
830+ | sndStep h => exact absurd h (value_no_step (.pair ha hb))
831+ | sndPair _ _ => rfl
832+ | echoAddLeft hs ih => cases hs₂ with
833+ | echoAddLeft h => exact congrArg (Expr.echoAdd · _) (ih h)
834+ | echoAddRight hv₁ _ => exact absurd hs (value_no_step hv₁)
835+ | echoAddNums => exact absurd hs (value_no_step (.num _))
836+ | echoAddRight hv₁ hs ih => cases hs₂ with
837+ | echoAddLeft h => exact absurd h (value_no_step hv₁)
838+ | echoAddRight _ h => exact congrArg (Expr.echoAdd _ ·) (ih h)
839+ | echoAddNums => exact absurd hs (value_no_step (.num _))
840+ | echoAddNums => cases hs₂ with
841+ | echoAddLeft h => exact absurd h (value_no_step (.num _))
842+ | echoAddRight _ h => exact absurd h (value_no_step (.num _))
843+ | echoAddNums => rfl
717844
718845-- ═══════════════════════════════════════════════════════════════════════
719846-- COROLLARY: TYPE SAFETY
@@ -786,6 +913,25 @@ theorem echo_roundtrip_typed (e : Expr) (n : Nat) (h : HasType [] e (.word n)) :
786913 HasType [] (.lower (.echoClose e)) (.word 0 ) :=
787914 ⟨.tResidue _ _ _ _ (.tEchoClose _ _ n h), .tLower _ _ _ _ (.tEchoClose _ _ n h)⟩
788915
916+ /-- `echoAdd` recovers the summands: `add` discards which numbers were added,
917+ but the residue retains the pair. -/
918+ theorem echoAdd_residue_recovers (n₁ n₂ : Int) :
919+ StepStar (.residue (.echoAdd (.num n₁) (.num n₂))) (.pair (.num n₁) (.num n₂)) :=
920+ .head (.residueStep .echoAddNums) (.head (.residueVal (.pair (.num _) (.num _)) (.num _)) .refl)
921+
922+ /-- `lower ∘ echoAdd` is ordinary addition (the lossy result). -/
923+ theorem echoAdd_lower_sums (n₁ n₂ : Int) :
924+ StepStar (.lower (.echoAdd (.num n₁) (.num n₂))) (.num (n₁ + n₂)) :=
925+ .head (.lowerStep .echoAddNums) (.head (.lowerVal (.pair (.num _) (.num _)) (.num _)) .refl)
926+
927+ /-- **Echo distinguishes what `add` collapses.** 1+3 and 2+2 both lower to 4,
928+ but their residues (the summand pairs) stay distinct. -/
929+ theorem echoAdd_distinguishes :
930+ (StepStar (.lower (.echoAdd (.num 1 ) (.num 3 ))) (.num 4 ) ∧
931+ StepStar (.lower (.echoAdd (.num 2 ) (.num 2 ))) (.num 4 )) ∧
932+ (Expr.pair (.num 1 ) (.num 3 ) ≠ Expr.pair (.num 2 ) (.num 2 )) :=
933+ ⟨⟨echoAdd_lower_sums 1 3 , echoAdd_lower_sums 2 2 ⟩, by decide⟩
934+
789935-- ═══════════════════════════════════════════════════════════════════════
790936-- TG-2: DECIDABILITY OF TYPE CHECKING
791937-- ═══════════════════════════════════════════════════════════════════════
@@ -846,6 +992,22 @@ def infer (Γ : Ctx) : Expr → Option Ty
846992 match infer Γ r, infer Γ v with
847993 | some ρ, some τ => some (.echo ρ τ)
848994 | _, _ => none
995+ | .pair a b =>
996+ match infer Γ a, infer Γ b with
997+ | some α, some β => some (.prod α β)
998+ | _, _ => none
999+ | .fst e =>
1000+ match infer Γ e with
1001+ | some (.prod α _) => some α
1002+ | _ => none
1003+ | .snd e =>
1004+ match infer Γ e with
1005+ | some (.prod _ β) => some β
1006+ | _ => none
1007+ | .echoAdd e₁ e₂ =>
1008+ match infer Γ e₁, infer Γ e₂ with
1009+ | some .num, some .num => some (.echo (.prod .num .num) .num)
1010+ | _, _ => none
8491011
8501012/-- **Completeness** : every typing derivation is computed by `infer`. -/
8511013theorem infer_complete {Γ : Ctx} {e : Expr} {τ : Ty} :
@@ -914,6 +1076,23 @@ theorem infer_sound {Γ : Ctx} {e : Expr} {τ : Ty} :
9141076 next ρ τ' he_r he_v =>
9151077 injection h with h; subst h; exact .tEchoVal _ _ _ ρ τ' (ihr he_r) (ihv he_v)
9161078 all_goals simp at h
1079+ | pair a b iha ihb =>
1080+ intro h; simp only [infer] at h; split at h
1081+ next α β he_a he_b =>
1082+ injection h with h; subst h; exact .tPair _ _ _ α β (iha he_a) (ihb he_b)
1083+ all_goals simp at h
1084+ | fst e ih =>
1085+ intro h; simp only [infer] at h; split at h
1086+ next α β he => injection h with h; subst h; exact .tFst _ _ α β (ih he)
1087+ all_goals simp at h
1088+ | snd e ih =>
1089+ intro h; simp only [infer] at h; split at h
1090+ next α β he => injection h with h; subst h; exact .tSnd _ _ α β (ih he)
1091+ all_goals simp at h
1092+ | echoAdd e₁ e₂ ih₁ ih₂ =>
1093+ intro h; simp only [infer] at h; split at h
1094+ next he₁ he₂ => injection h with h; subst h; exact .tEchoAdd _ _ _ (ih₁ he₁) (ih₂ he₂)
1095+ all_goals simp at h
9171096
9181097/-- **Decidability of type checking** (TG-2): `infer` decides `HasType`. -/
9191098theorem infer_iff_hasType {Γ : Ctx} {e : Expr} {τ : Ty} :
0 commit comments