feat(#92): widths need not agree for ==, and match arms join on width - #95
Conversation
Implements the #92 ruling across both engines, plus two bugs found while verifying it. ## The rule `T-Eq-Word` required both operands at the same width. That was inconsistent with the rest of the language in four ways, all recorded in #92: * `tComposeWord` already WIDENS to max n m — a program could compose two braids it was then forbidden to compare; * `~` accepted differing widths and, since TG-7, evaluates through the SAME `Braid_equiv.equiv` — identical operands, identical answer, one rejected; * `eqIdBraid`/`eqBraidId` decide "is this braid trivial?" against `identity : word 0`, so under the old rule they could only ever fire when the braid was empty — the step relation had rules for a question the typing rule forbade asking; * `braid_equiv.equiv` has no width parameter at all. Lean `tEqWord` now takes (n m); `infer`'s `.eq` case drops the `if n = m`. OCaml `Eq` mirrors it. Match arms join on width for words (max) and still require exact agreement for everything else. ## The metatheory did not need patching Progress, Preservation, Determinism, TypeSafety, infer_sound and infer_complete all still hold — `lean Tangle.lean` reports 0 errors, and the sorry/axiom gate passes. The `infer_sound` proof got SIMPLER: with no `if n = m` there is no inner `split` left to case on, which is a decent sign the restriction was load-bearing for nothing. TG-3 regenerated and kernel-checked: 496 obligations, 0 errors. 46 of them flipped from `= none` to `= some .bool` — precisely the `identity == braid` shape that was previously unreachable. ## Two bugs found while verifying 1. STATEMENT ORDER. `parse_file_recovering` in bin/main.ml did `stmts := prog @ !stmts` and then `List.rev` on the flattened result. That reversal is right for an accumulator built by prepending single items — as `diagnostics` is — but whole SEGMENTS were prepended, so every program came out backwards: `def x; def y; def z` parsed to [z; y; x]. Any program whose statements depend on order failed at evaluation with "Unbound variable". The test suites never caught it because they call `Tangle.Parser.program` directly; only the CLI goes through the recovering path. Now accumulates segments and flattens in order. 2. A FALSE ASSERTION in examples/trefoil.tangle: assert reversed == braid[s1, s1, s1] `reverse` reverses the word AND negates exponents, so it yields the INVERSE braid. writhe(trefoil) = 3 but writhe(reversed) = -3, and writhe is invariant under the braid relations, so they cannot be equal. The assertion had been wrong since it was written; nothing ran the examples until #89. Corrected to braid[s1^-1, s1^-1, s1^-1], which passes. ## Result examples evaluating : 2/7 -> 7/7 (all now in the must-run set) lib/stdlib.tangle : did not typecheck -> typechecks conformance : 18/19 (v11 add{} is the Harvard sub-language, #94) ## Trusted base Registered as A-TG-92.1: the embedding Bn -> Bn+1 that justifies cross-width comparison is standard mathematics but is asserted IN PROSE, not mechanised. What is machine-checked is that the metatheory holds under the widened rule and that OCaml still agrees with Lean on the corpus. Tests: 6 new typecheck cases including two negatives (mismatched KINDS still rejected; match arms of different kinds still rejected) so widening cannot degrade into "anything compares to anything". Three test_check cases repointed at an error that is still an error. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Note Automatic reviews are paused because your trial's included automatic processing has been used for this period. Upgrade now, or comment "Gitar review" to run a review anytime. Code Review ✅ ApprovedRemoves strict width agreement requirements for equality comparisons and adds match arm width joining across both engines, enabling all 7 example programs to evaluate successfully. No issues found.
OptionsDisplay: compact → Showing less information. Comment with these commands to change the behavior for this request:
Was this helpful? React with 👍 / 👎 | Gitar |
|
|
Implements the #92 ruling across both engines — plus two bugs found while verifying it.
The rule
T-Eq-Wordrequired both operands at the same width. Four ways that was inconsistent with the language itself (evidence in #92):tComposeWordalready widens tomax n m— you could compose two braids you were then forbidden to compare.~accepted differing widths and, since TG-7, evaluates through the sameBraid_equiv.equiv. Identical operands, identical answer, one rejected by the typechecker.eqIdBraid/eqBraidIddecide "is this braid trivial?" againstidentity : word 0, so under the old rule they could only fire when the braid was empty. The step relation had rules for a question the typing rule forbade asking.braid_equiv.equivhas no width parameter at all.Lean's
tEqWordnow takes(n m);infer's.eqcase drops theif n = m; OCamlEqmirrors it. Match arms join on width for words and still require exact agreement otherwise.The metatheory needed no patching
Progress, Preservation, Determinism, TypeSafety,
infer_sound,infer_completeall still hold —lean Tangle.leanreports 0 errors, sorry/axiom gate passes.infer_sound's proof got simpler: with noif n = mthere's no innersplitto case on. A restriction whose removal shortens the proof was carrying no weight.TG-3 regenerated and kernel-checked: 496 obligations, 0 errors. 46 flipped from
= noneto= some .bool— exactly theidentity == braidshape that was unreachable before.Two bugs found while verifying
1. Statement order.
parse_file_recoveringdidstmts := prog @ !stmtsthenList.revon the flattened result. That reversal is correct for an accumulator built by prepending single items (asdiagnosticsis) — but whole segments were prepended, so every program came out backwards:Any program whose statements depend on order died with "Unbound variable". The test suites never caught it because they call
Tangle.Parser.programdirectly — only the CLI goes through the recovering path.2. A false assertion in
examples/trefoil.tangle:reversereverses the word and negates exponents, yielding the inverse braid. Verified by invariant:writhe(trefoil) = 3,writhe(reversed) = -3, and writhe is invariant under the braid relations — so they cannot be equal. The assertion had been wrong since it was written; nothing ran the examples until #89. Corrected tobraid[s1^-1, s1^-1, s1^-1], which passes.Result
lib/stdlib.tanglev11is the Harvard sub-language, #94)All seven examples are now in the corpus gate's must-run set, so none can regress.
⚠ Trusted base — registered as A-TG-92.1
The embedding Bₙ ↪ Bₙ₊₁ that justifies cross-width comparison is standard mathematics, but it is asserted in prose, not mechanised — no Lean lemma states it.
What is machine-checked: that the metatheory holds under the widened rule, and that OCaml
infer_exprstill agrees with Leaninferon the corpus. That distinction is recorded inASSUMPTIONS.mdrather than left implicit.Tests
6 new typecheck cases, including two negatives — mismatched kinds still rejected, match arms of different kinds still rejected — so widening cannot quietly become "anything compares to anything". Three
test_checkcases repointed at an error that is still an error (and an assertion helper left unused by the change was put back to work rather than deleted).🤖 Generated with Claude Code