Iter 14g: Term::If restored (revert of 14d)
Reconsidered 14d's removal of Term::If. The decision was wrong.
"No redundancies" requires judgment; reducibility (if -> match)
is not redundancy in the strong sense. Term::If is a primitive
control-flow shape; bool branching is the second most common
shape after sequencing, and removing it cost 3x tokens on every
branch site (`(if c a b)` 4 tokens vs the match-on-Bool form
12 tokens).
Meta-pattern fixed: I had been treating user observations as
directives. User said "if is a subset of match"; I jumped to
remove it citing CLAUDE.md, with no independent conviction.
The leak appeared in 14f's JOURNAL prose ("three lines for what
if used to do in one"), which read as regret. Two feedback
memories saved (memory/feedback_user_suggestions_not_directives,
memory/feedback_no_nostalgia_for_removed_features) to head this
off.
Implementation: mechanical reverse-application of 14d's diff at
every site (AST, check including the 14e tail-position arm,
codegen 4 sites, surface parser/printer, pretty, CLI walker,
e2e test mutation). Removed lower_bool_match helper — it existed
only because 14d's migration shape needed codegen for non-ptr
match scrutinees; with Term::If back, match-on-Bool returns to
its pre-14d unsupported state. Three fixtures (sum, sort, max3)
restored to pre-14d shape. gc_stress (added in 14f) also
migrated back to (if ...) since it was authored under the wrong
constraint.
14e (musttail) and 14f (GC_malloc) verified intact in IR.
Hashes restored to pre-14d values:
- sum.sum: db33f57cb329935e
- sort.insert: 697fcb9f30f8633a
- max3.max: 65c45d6a45dd0a72
- max3.max3: 624b14429bf302f5
All other defs across all 18 fixtures keep their post-14f
hashes. Tests 80/80 green; cargo doc 0 warnings. LOC delta
+265/-295 net -30.
DESIGN.md Decision 7 preserved with a "Status: REVERTED" header
for audit trail. Form-(A) `if-term` production restored.
Plan: back to 15a (std_maybe stdlib module).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+9
-3
@@ -446,6 +446,13 @@ JSON identical to their corresponding `.ail.json` files.
|
||||
|
||||
## Decision 7: redundancy removal — `Term::If` is not a primitive
|
||||
|
||||
**Status: REVERTED in Iter 14g.** This decision was made on shaky grounds —
|
||||
applying CLAUDE.md's "no redundancies" rule to a case that turned out to
|
||||
be primitive control flow, not redundancy. The post-removal match-on-Bool
|
||||
form (3× the tokens, asymmetric `pat-wild` for the false case) was
|
||||
worse for token economy and worse for the natural shape of the language.
|
||||
`Term::If` is restored. The text below is preserved for the audit trail.
|
||||
|
||||
`Term::If { cond, then, else_ }` is semantically a subset of
|
||||
`Term::Match` on `Bool`. Per CLAUDE.md the language must contain no
|
||||
redundancies; two AST nodes for the same operation produces an
|
||||
@@ -645,6 +652,7 @@ hashes stay bit-identical.
|
||||
{ "t": "var", "name": "<id>" }
|
||||
{ "t": "app", "fn": Term, "args": [Term...] }
|
||||
{ "t": "let", "name": "<id>", "value": Term, "body": Term }
|
||||
{ "t": "if", "cond": Term, "then": Term, "else": Term }
|
||||
{ "t": "do", "op": "<eff>/<op>", "args": [Term...] }
|
||||
{ "t": "ctor", "type": "<id>", "ctor": "<id>", "args": [Term...] }
|
||||
{ "t": "match", "scrutinee": Term, "arms": [Arm...] }
|
||||
@@ -740,9 +748,7 @@ as iterations land; the JOURNAL records the exact iteration.
|
||||
What **is** supported (and used as the smoke test for the pipeline):
|
||||
|
||||
- Int, Bool, Unit, **Str** as primitive types.
|
||||
- `let`, function calls, recursion. Bool branching is expressed via
|
||||
`match` on `Bool` with a `(lit-bool true)` arm and a wildcard
|
||||
fallback (Decision 7); there is no separate `if` AST node.
|
||||
- `if`, `let`, function calls, recursion.
|
||||
- Effects on function signatures, with `do op(args)` for direct effect
|
||||
ops (`io/print_int`, `io/print_bool`, `io/print_str`).
|
||||
- **ADTs + flat pattern matching** (Iter 3). Sub-patterns of a Ctor
|
||||
|
||||
@@ -2098,6 +2098,99 @@ under `examples/std/` as `.ailx` source; tests load the
|
||||
generated `.ail.json`. `ailang-check` and `ailang-codegen`
|
||||
remain projection-agnostic.
|
||||
|
||||
## Iter 14g — `Term::If` restored (revert of 14d)
|
||||
|
||||
Reconsidered 14d's removal of `Term::If`. The decision was wrong;
|
||||
restored.
|
||||
|
||||
**Why 14d was wrong.** "No redundancies" from CLAUDE.md is a real
|
||||
rule but it requires judgment to apply. `Term::If` reduces to
|
||||
`Term::Match` on Bool, but reducibility is not redundancy in a
|
||||
strong sense — `1 + 1` reduces to `2`, you don't remove `+` from
|
||||
the language because of it. `Term::If` is a primitive control-
|
||||
flow shape that every programming language has for good reason:
|
||||
bool branching is the second most common control flow shape after
|
||||
sequencing.
|
||||
|
||||
**Quantitative.** `(if c a b)` is 4 tokens. The post-14d
|
||||
replacement `(match c (case (pat-lit true) a) (case (pat-wild) b))`
|
||||
is 12. That's a 3× token-economy hit on every Bool branch — in
|
||||
exactly the language whose authoring constraint was supposed to
|
||||
be token-efficient. The match-on-Bool form is also asymmetric
|
||||
(false case via `pat-wild` because the typechecker rejects
|
||||
`pat-lit false` as exhaustive) and structurally lopsided.
|
||||
|
||||
**Meta-pattern that produced the wrong call.** I had been treating
|
||||
user observations as directives. The user said "if is a subset of
|
||||
match" — which is a factual observation; I jumped to remove it,
|
||||
citing CLAUDE.md as cover. There was no independent conviction
|
||||
behind the change, only doctrinal hooking-up of a user remark.
|
||||
The leak showed up in 14f's JOURNAL prose ("three lines for what
|
||||
`if` used to do in one"), which the user correctly read as me
|
||||
regretting the decision.
|
||||
|
||||
Two feedback memories saved to head this off in future iters
|
||||
(`/home/brummel/.claude/projects/-home-brummel-dev-ailang/memory/`):
|
||||
- `feedback_user_suggestions_not_directives.md` — observations
|
||||
are input, not output. Form an opinion before acting.
|
||||
- `feedback_no_nostalgia_for_removed_features.md` — describe
|
||||
canonical form on its own merits, not as compensation for
|
||||
what was deleted.
|
||||
|
||||
**Implementation (revert).** Mechanically reverse-applied 14d's
|
||||
diff at every site (`ast.rs`, `check/lib.rs` (incl. the new
|
||||
14e `verify_tail_positions` arm), `codegen/lib.rs` (4 sites),
|
||||
`surface/{parse,print}.rs`, `core/pretty.rs`, `ail/main.rs`,
|
||||
`e2e.rs` test mutation). Removed the `lower_bool_match` helper
|
||||
that 14d had introduced — it existed only because the 14d
|
||||
migration shape needed codegen for non-`ptr` match scrutinees;
|
||||
with `Term::If` back, match-on-Bool returns to its pre-14d
|
||||
unsupported state and the helper is dead weight. Three fixtures
|
||||
(`sum`, `sort`, `max3`) restored to their pre-14d shape.
|
||||
|
||||
**One additional fixture migration.** `gc_stress` was authored
|
||||
in 14f using the 14d match-on-Bool migration shape (because
|
||||
14f sat between 14d and this revert). After removing
|
||||
`lower_bool_match` it would have failed to compile. Migrated
|
||||
`gc_stress.{ail.json,ailx}` to use `(if ...)` directly. Output
|
||||
unchanged: `1275`.
|
||||
|
||||
**14e and 14f are intact.** Verified by spot-emit of
|
||||
`list_map_poly`'s IR: `musttail call i8 @ail_list_map_poly_print_list`
|
||||
and `call ptr @GC_malloc(i64 8)` both present. The revert is
|
||||
strictly local to `Term::If`-related code paths.
|
||||
|
||||
**Hash check.** All four pre-14d hashes returned:
|
||||
|
||||
| def | restored hash |
|
||||
|---|---|
|
||||
| `sum.sum` | `db33f57cb329935e` |
|
||||
| `sort.insert` | `697fcb9f30f8633a` |
|
||||
| `max3.max` | `65c45d6a45dd0a72` |
|
||||
| `max3.max3` | `624b14429bf302f5` |
|
||||
|
||||
Untouched defs across all 18 fixtures (incl. the 14e print_list
|
||||
hash deltas) keep their post-14f hashes. The revert's hash
|
||||
movement is exactly the four 14d-migrated defs reverting plus
|
||||
the one accidental 14f-victim (`gc_stress` defs, never previously
|
||||
shipped under any other hash).
|
||||
|
||||
**DESIGN.md.** Decision 7 is preserved with a `Status: REVERTED`
|
||||
header. Audit trail matters; future reads should see the
|
||||
decision and its reversal both. Form-(A) productions in
|
||||
Decision 6's appendix have `if-term` restored.
|
||||
|
||||
**Tests: 80/80 green.** Identical stdout for every existing
|
||||
fixture. `cargo doc --no-deps` 0 warnings.
|
||||
|
||||
**LOC delta.** +265/-295 net −30. Net cleanup: the `lower_bool_match`
|
||||
helper was bigger than the restored `Term::If` codegen.
|
||||
|
||||
**Plan.** Back to 15a — first stdlib module `std_maybe`. The
|
||||
brief I had drafted included an "authoring note: post-14d
|
||||
if-then-else" section that's now obsolete. Re-issue without
|
||||
that, using `if` naturally where appropriate.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user