276 lines
14 KiB
Markdown
276 lines
14 KiB
Markdown
# JUDGMENT_CALLS — semantic decisions awaiting ratification
|
||
|
||
RATIFIED 2026-07-09 — this file is the semantic record of MPL M0.
|
||
|
||
Every section below is a semantic question Stage 2 surfaced, stated with
|
||
the behavior `js/mpl.js` exhibited at observation time (Stage 2 head,
|
||
`ea66a2a`) and the corpus entries that pinned it. Each `RULING:` line
|
||
records Reverend's ratified decision of 2026-07-09. BLESS rulings kept the
|
||
observed behavior; OVERRIDE/SPLIT/IMPLEMENT/AMEND rulings changed the
|
||
grammar or the interpreter in Stage 3, and the corpus was re-recorded to
|
||
match. The "Observed:" text is the historical record, not the current
|
||
behavior — the rulings govern.
|
||
|
||
## 1. Division by zero
|
||
|
||
Question: what does `x ÷ 0` do — error, infinity, or ⊥?
|
||
Observed: raises `err_div0` (both `÷` and `/`, integer or float operands).
|
||
Pins: `043_div_zero`.
|
||
|
||
RULING: BLESS. `x ÷ 0` (and `/`) raises `err_div0`. Division by zero is undefined.
|
||
|
||
## 2. The value of a ∀ expression
|
||
|
||
Question: what does `∀ x ∈ list : body` evaluate to?
|
||
Observed: the value of the last body evaluation; `⊥` for an empty list.
|
||
Pins: `018_forall_value`, `019_forall_scope`.
|
||
|
||
RULING: OVERRIDE. `∀` is an iterator; the expression's value is `⊥` always (including empty collections). Using a statement as a value is undefined — same principle as ruling 3.
|
||
|
||
## 3. Result when no guard matches
|
||
|
||
Question: what is `(false ⟹ e)` with no `|` fallback?
|
||
Observed: an internal no-match that surfaces as `⊥` everywhere except
|
||
directly to the left of `|`.
|
||
Pins: `021_no_guard_match`, `023_alt_chain`.
|
||
|
||
RULING: BLESS (semantics, not mechanism). An unmatched guard yields `⊥` unless caught by `|`. Implementations choose their own internal sentinel.
|
||
|
||
## 4. Integer vs float display
|
||
|
||
Question: is `4 ÷ 2` shown as `2` or `2.0`? Are all numbers one type?
|
||
Observed: all numbers are IEEE doubles; integral values display with no
|
||
decimal point (`2`), non-integral as JS renders them (`0.5`, and
|
||
`0.1 + 0.2` shows `0.30000000000000004`). Literals normalize (`007` → `7`,
|
||
`0.50` → `0.5`).
|
||
Pins: `006_number_display`, `007_float_arithmetic`.
|
||
|
||
RULING: OVERRIDE. Numbers are exact rationals (arbitrary-precision integer numerator/denominator). Decimal literals convert exactly (`0.1` = 1/10). Display: integers bare; otherwise lowest-terms fraction `num/den`, sign on the numerator, denominator > 0; zero as `0`. Output is valid MPL that re-evaluates to the same value. IEEE doubles are rejected: the language must not lie about `0.1`. `√ vs ℚ` is logged as a standing M1 decision.
|
||
|
||
## 5. ✎ formatting per type
|
||
|
||
Question: exactly how does `✎` render each value type?
|
||
Observed: numbers via host `String()`; strings bare at top level but
|
||
quoted inside lists; booleans `true`/`false`; lists `[a, b]` with
|
||
one-space separation; closures as `λ`; `⊥` as `⊥`.
|
||
Pins: `009_string_concat`, `011_list_display`, `031_show_all_types`.
|
||
|
||
RULING: BLESS, spec'd. `✎` renders: numbers per ruling 4; strings bare at top level, double-quoted inside lists; booleans `true`/`false`; lists `[a, b]` (comma-space); closures as `λ`; `⊥` as `⊥`.
|
||
|
||
## 6. ⊥ display and propagation
|
||
|
||
Question: is `⊥` a first-class value or a poison that propagates?
|
||
Observed: a first-class value — it displays as `⊥`, concatenates into
|
||
strings (`"x" + ⊥` → `x⊥`), sits in lists, but arithmetic on it is
|
||
`err_num`.
|
||
Pins: `030_bot`, `052_bot_arith`.
|
||
|
||
RULING: BLESS. `⊥` is first-class: displays as `⊥`, sits in lists, string concatenation renders it (`"x" + ⊥` → `x⊥`); arithmetic on it is `err_num`.
|
||
|
||
## 7. Equality across types
|
||
|
||
Question: what does `=` mean across types and on structures?
|
||
Observed: structural (JSON-serialization) equality — lists compare
|
||
element-wise, cross-type comparisons are `false` (`1 = "1"` → `false`),
|
||
and `⊥ = ⊥` → `true`. Comparing a self-referential closure crashes with a
|
||
keyless host error (unpinnable by the corpus).
|
||
Pins: `034_cross_type_equality`, `033_string_compare`.
|
||
|
||
RULING: SPLIT. Data equality is structural: element-wise lists, cross-type `=` is `false`, `⊥ = ⊥` is `true`, `0.5 = 1/2` is `true` (same rational). Any equality comparison involving a function raises `err_fn_eq` — function equality is undecidable; a keyed error replaces the current keyless crash.
|
||
|
||
## 8. Ordering across types
|
||
|
||
Question: what do `< > ≤ ≥` do on mixed or non-numeric operands?
|
||
Observed: raw host comparison with JS coercion — `1 < "2"` → `true`,
|
||
`"10" < 9` → `false`, `true < 2` → `true`; strings order lexicographically.
|
||
Pins: `035_mixed_compare`, `033_string_compare`.
|
||
|
||
RULING: OVERRIDE. `< > ≤ ≥` are defined on number×number and string×string (Unicode code-point order) only; anything else raises `err_compare`. Host coercion (`1 < "2"`) is JavaScript soul leakage, not mathematics.
|
||
|
||
## 9. Closure capture
|
||
|
||
Question: do closures capture values at definition time or the
|
||
environment?
|
||
Observed: the environment — later mutation of a captured variable is
|
||
visible on the next call (`013` prints 11 then 21).
|
||
Pins: `013_closure_capture`, `026_def_vs_assign_scope`.
|
||
|
||
RULING: BLESS. Closures capture the environment. (The corpus itself forces this: `factorial` only works because the λ sees its own later binding.)
|
||
|
||
## 10. Recursion depth
|
||
|
||
Question: what bounds recursion, and how does exceeding it surface?
|
||
Observed: the host JS stack — deep recursion dies as a keyless RangeError
|
||
before the 500 000-step budget can trigger, so the corpus cannot pin it
|
||
(no error key). Iteration-heavy programs do hit the budget and raise
|
||
`err_steps`. Depth ~500 is comfortably safe.
|
||
Pins: `016_recursion_sum` (works at 500), `053_step_budget` (`err_steps`).
|
||
|
||
RULING: OVERRIDE. Recursion is bounded by an explicit λ-application depth counter, limit 10 000, raising `err_depth`. A keyless host RangeError is a hole in the error surface; the limit is conformance surface in every implementation.
|
||
|
||
## 11. The step budget itself
|
||
|
||
Question: is "500 000 evaluation steps, then `err_steps`" part of the
|
||
language, and is that the right number?
|
||
Observed: hard-coded 500 000; nested `∀` loops over a 10-element list six
|
||
deep exceed it.
|
||
Pins: `053_step_budget`.
|
||
|
||
RULING: RECLASSIFY. The step budget is an environment resource limit, not language semantics — like out-of-memory. `err_steps` (500 000) remains in the browser implementation; entry 053 leaves the portable corpus and becomes a js/test implementation test.
|
||
|
||
## 12. String escape round-tripping
|
||
|
||
Question: which string escapes exist, and what does an unknown one mean?
|
||
Observed: `\n` and `\t` expand; `\"` and `\\` escape themselves; any other
|
||
`\x` silently drops the backslash (`"drop\qme"` → `dropqme`). The ANTLR
|
||
grammar instead REJECTS unknown escapes — a recorded divergence.
|
||
Pins: `008_string_escapes`; divergence in DIVERGENCES.md ("drop\qme" and
|
||
the `"a\zb"` fuzz entry).
|
||
|
||
RULING: OVERRIDE. Escapes are exactly `\n \t \" \\`; any other `\x` raises `err_escape`, matching the grammar. Silent data-mangling is forbidden.
|
||
|
||
## 13. Guard condition truthiness
|
||
|
||
Question: what may a guard condition be?
|
||
Observed: `true` and non-zero numbers fire the guard; `false`, `0`,
|
||
strings, lists and `⊥` do not (they yield the no-match path) — so numbers
|
||
are truthy for `⟹` but nothing else is.
|
||
Pins: `022_guard_truthiness`.
|
||
|
||
RULING: OVERRIDE. A guard condition must be boolean; any non-boolean condition (numbers included) raises `err_bool`. A condition is a proposition.
|
||
|
||
## 14. ∧ ∨ operand truth
|
||
|
||
Question: do `∧`/`∨` accept the same truthiness as `⟹`?
|
||
Observed: no — operands are tested with strict boolean equality, so
|
||
`1 ∧ true` → `false` while `(1 ⟹ x)` fires. Both operators short-circuit
|
||
(observable by side effect). This asymmetry with #13 is the sharpest
|
||
accident in the surface.
|
||
Pins: `036_logic_ops`, `037_logic_short_circuit`.
|
||
|
||
RULING: SPLIT. `∧ ∨` short-circuit (blessed, observable by side effect) and demand boolean operands — a non-boolean evaluated operand raises `err_bool` instead of silently comparing false. One notion of truth, everywhere; an unevaluated right operand raises nothing.
|
||
|
||
## 15. Block scoping
|
||
|
||
Question: does `{ … }` create a scope?
|
||
Observed: no — bindings made inside a brace block leak out (`028` prints
|
||
9). Only λ bodies and each `∀` iteration scope.
|
||
Pins: `028_block_no_scope`, `027_block_sequencing`.
|
||
|
||
RULING: BLESS. Braces group; they do not scope. Scope is created by binders only (λ parameters, ∀ iteration variables) — the Curry-Howard reading: a discharged hypothesis IS a λ. An explicit local-binding construct (let/where) is logged as a standing M1 decision.
|
||
|
||
## 16. ≜ vs ← scoping and creation
|
||
|
||
Question: how do define and assign differ?
|
||
Observed: `≜` always binds in the current scope; `←` mutates the nearest
|
||
enclosing binding and silently creates one in the current scope when
|
||
nothing is bound (assignment-before-definition is legal). Both are
|
||
expressions returning the value; both rebind freely (`x ≜ 1; x ≜ 3` is
|
||
legal re-definition).
|
||
Pins: `024_def_assign_rebind`, `025_def_assign_value`,
|
||
`026_def_vs_assign_scope`.
|
||
|
||
RULING: OVERRIDE (both halves). `≜` introduces a name exactly once per scope — same-scope redefinition raises `err_redef`; shadowing in inner scopes is legal. `←` mutates the nearest enclosing binding and raises `err_unbound` when none exists — silent creation is the typo trap. Both remain expressions returning the value.
|
||
|
||
## 17. Type constraints
|
||
|
||
Question: does `λx∈ℕ:` constrain anything?
|
||
Observed: the constraint parses (including supplementary-plane `𝔹`) and
|
||
is discarded unevaluated — `g ≜ λs∈𝔹: s + "!"` happily takes a string.
|
||
Matches the site's "parsed today, not yet enforced" claim.
|
||
Pins: `039_type_constraint_unenforced`.
|
||
|
||
RULING: BLESS. Type constraints parse and are discarded, unenforced — this is the published claim and Stage 5's mandate.
|
||
|
||
## 18. Nullary calls and zero-parameter λ
|
||
|
||
Question: `f()` parses — but can any function be called that way?
|
||
Observed: no zero-parameter λ can be written (`λ: e` is a parse error),
|
||
so every `f()` is a runtime `err_arity`. The call syntax exists; nothing
|
||
can satisfy it.
|
||
Pins: `046_arity_nullary`, `047_arity_extra`.
|
||
|
||
RULING: AMEND GRAMMAR. Nullary functions exist: `λ: e` is admitted (bare colon; the parenthesized spelling stays rejected per ruling 25). Effects made M0 procedural the day `✎` entered it; arity 0 is not an exception to ℕ. `f()` calls it; arity mismatches remain `err_arity`.
|
||
|
||
## 19. ∗ as multiplication
|
||
|
||
Question: is `∗` (U+2217) an operator, and what does it mean?
|
||
Observed: exactly `×` — same token, same precedence.
|
||
Pins: `041_asterisk_multiplication`.
|
||
|
||
RULING: BLESS. `∗` (U+2217) is exactly `×`.
|
||
|
||
## 20. Divergence: empty program
|
||
|
||
Question: is the empty program valid?
|
||
Observed: the grammar accepts it; the JS interpreter rejects it
|
||
(`err_unexpected` at 1:1).
|
||
Pins: none possible until ruled (decision 3 — divergent programs cannot
|
||
enter the corpus). Repro in DIVERGENCES.md (fuzz index 49).
|
||
|
||
RULING: OVERRIDE. The empty program is valid and produces no output. The grammar is right; the interpreter accepts it.
|
||
|
||
## 21. Divergence: sequences inside parentheses
|
||
|
||
Question: is `( e1 ; e2 )` valid? DECISIONS.md says `(...)` contains one
|
||
seqExpr; the JS parser allows exactly one expression.
|
||
Observed: the grammar accepts `(42;)` and `(a; b)`; the JS interpreter
|
||
rejects both (`err_expect`).
|
||
Pins: none until ruled. Repros in DIVERGENCES.md (fuzz indexes 86, 119,
|
||
247, 233, 279, 406, 468, 101…).
|
||
|
||
RULING: OVERRIDE. Parentheses contain one seqExpr per the standing DECISIONS ruling: `(e1; e2)` is legal, value = last expression. The interpreter implements its own language.
|
||
|
||
## 22. Divergence: `∘` composition
|
||
|
||
Question: does function composition exist in M0?
|
||
Observed: the grammar parses `f ∘ g`; the JS interpreter lexes `∘` (it
|
||
even has the `\circ` escape) but has no parse rule for it — every use is
|
||
`err_expect`.
|
||
Pins: none until ruled. Repro in DIVERGENCES.md (fuzz index 9).
|
||
|
||
RULING: IMPLEMENT. `∘` is function composition: `(f ∘ g)(args…) = f(g(args…))`. Operands must be functions — a non-function operand raises the expected-a-function key (reuse the existing key if the audit finds one, else introduce `err_fn`). Precedence and associativity follow the grammar.
|
||
|
||
## 23. Divergence: set literals
|
||
|
||
Question: `{a, b}` is a set per the grammar's brace disambiguation — does
|
||
the M0 core have sets?
|
||
Observed: the grammar accepts `({5, "a b"})` and friends; the JS
|
||
interpreter has no set support and rejects them (`err_expect`).
|
||
Pins: none until ruled. Repros in DIVERGENCES.md (multiple fuzz indexes).
|
||
|
||
RULING: PARSE-ONLY. Set literals parse (the interpreter gains the grammar's brace disambiguation) and evaluation raises `err_notyet`. Set semantics are an M1 design, logged.
|
||
|
||
## 24. Divergence: record literals
|
||
|
||
Question: `{k: v}` is a record per the grammar — does the M0 core have
|
||
records?
|
||
Observed: the grammar accepts `({y: 0})`; the JS interpreter rejects
|
||
(`err_expect`).
|
||
Pins: none until ruled. Repros in DIVERGENCES.md (fuzz indexes 25, 212,
|
||
425, 427, 447).
|
||
|
||
RULING: PARSE-ONLY. Record literals: same as 23.
|
||
|
||
## 25. Divergence: parenthesized λ parameters
|
||
|
||
Question: DECISIONS.md rejected `λ(a, b):` as a second parameter spelling
|
||
— but who enforces that?
|
||
Observed: inverted from every other divergence — the GRAMMAR rejects
|
||
`λ(a, b): e` (honoring the decision) while the JS interpreter accepts it.
|
||
The interpreter carries a syntax form the language explicitly rejected.
|
||
Pins: none until ruled. Repros in DIVERGENCES.md (fuzz indexes 13, 29,
|
||
76, …).
|
||
|
||
RULING: OVERRIDE. `λ(a, b): e` is rejected by the interpreter too — the grammar already enforces the ratified DECISIONS ruling.
|
||
|
||
## 26. Divergence: bare λ and Greek letters as identifiers
|
||
|
||
Question: the grammar tokenizes Greek letters (LAMBDA_VAR et al.) as
|
||
identifiers, so `{λ}` parses; the JS interpreter only knows λ as the
|
||
abstraction head.
|
||
Observed: grammar accepts `({λ})`; JS rejects (`err_expect` — λ demands a
|
||
parameter list).
|
||
Pins: none until ruled. Repro in DIVERGENCES.md (fuzz index 52).
|
||
|
||
RULING: AMEND GRAMMAR. `λ` is a reserved token, never an identifier. Only λ; other Greek letters (π et al.) remain identifiers — Fatima wants π.
|