Skip to content

Destructuring (RG-4b): live in declarations, snapshots in let - #49

Merged
ryansolid merged 10 commits into
mainfrom
claude/project-thread-2sdg89-destructuring
Nov 11, 2026
Merged

ryansolid merged 10 commits into
mainfrom
claude/project-thread-2sdg89-destructuring

Conversation

@ryansolid

@ryansolid ryansolid commented Nov 11, 2026 •

Copy link
Copy Markdown
Member

Implements the destructuring RG-4b already promised, rest included. Each side keeps its meaning:

component Card({ title, open = false, ...rest }) {   // each name a projection of props, stays live
  { a, b: bee } = rest.pair                           // one equation per name
  [first, , third = "z", ...more] = rest.list
  <button onClick={() => {
    let { a: q, ...r } = pair                         // imperative: snapshots (a rest is a copy), as in JS
    pair := { a: r.b, b: q }                          // only := and guess mutate
  }}>{title}</button>
}
  • Equations vs snapshots: bare x = e is always an equation and let x = e always a snapshot, destructured or not. A destructured name (a rest included) is an equation, so a := v is WRITE_TO_NON_SOURCE.
  • Object rest: { a, ...rest } = e lowers to Solid's omit(e, "a"), a live view, so rest.pair stays props.pair. In a let it lowers to { ...omit(…) }, a copy. This uses a new internal omit expression, handled in the interpreter, oracle, compiled output, type projection and the Rust crate.
  • Array rest: [x, ...rest] = e lowers to e.slice(1). A rest must be last.
  • Defaults apply on undefined.
  • Right sides that aren't paths: a right side that isn't a name or a path is bound once to a hidden equation, so it is only evaluated once.
  • JS_API hint: [n, setN] = createSignal(0) still gets the JS_API hint.
  • Tests and docs: new scenario S-destructuring. Docs: the 07 grammar and decision 20, and guide/language.md.

Tests: interpreter, oracle and compiled pass 125/125; types 171; ssr 118; crate parity 351/351; lowering 206/206; cargo test green.

🤖 Generated with Claude Code

https://claude.ai/code/session_0117BoWP7LmQhib6M9zGnmBH

`guess x := e` replaces `optimistic x := e` (07 decision 18, ruled
2026-11-11). Semantics and lowering are unchanged (createOptimistic /
createOptimisticStore); only the surface word moves.

- Parsers (ref/src/parse.ts, compiler/src/parse.rs): `guess` is the
  keyword; `optimistic` stays reserved and is a parse error coded
  OPTIMISTIC whose hint names `guess` (guide/diagnostics.md, S-parse-hints).
- Codes: OPTIMISTIC_OUTSIDE_HOLD -> GUESS_OUTSIDE_HOLD,
  OPTIMISTIC_NO_QUESTION -> GUESS_NO_QUESTION, OPTIMISTIC_REVERTED ->
  GUESS_REVERTED; HELD_WRITE / STALE_WRITE_BACK / AC-1b messages say `guess`.
- Spec, guide, scenarios, examples, eval reference apps and the editor
  grammar use `guess`. eval/results and preregistrations are left as
  historical records.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117BoWP7LmQhib6M9zGnmBH
Drops the OPTIMISTIC parse hint, its guide section, spec row and
S-parse-hints variant; 07 decision 18 says why.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117BoWP7LmQhib6M9zGnmBH
An error clear is a reset that lands now (EF-8), not a guess at the
server's answer: `guess saveError := ""` worked only because the source
was given a constant question to revert to. The guide's action example,
the examples and the eval reference apps now write
`onFlush(() => { saveError := "" })`, and those error sources are
declared with `:=`. S-ai2-settings gains the flush instant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117BoWP7LmQhib6M9zGnmBH
`x = e` then `guess x := v` in an action is legal (06 ST-5c, 07 decision
19): a guess is not a write of the truth, and Solid's derived
createOptimistic(fn) has a setter that only guesses. A plain `x := v` on
an equation stays WRITE_TO_NON_SOURCE; `props.x` and parameters stay
refused (O-4). New code GUESS_ON_DEFERRED: a deferred equation lags its
truth (V-16).

Lowering: a guessed equation is createOptimistic(fn), or
createOptimisticStore(fn, seed) when keyed or guessed by path, on the
client, the server (matching owner trees for hydration) and in the oracle.
Both checkers and both emitters agree (crate parity, lowered alike).
Scenario S-guess-equation covers an equation of a source and a child's
equation of a prop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117BoWP7LmQhib6M9zGnmBH
…ponent signatures, `let { … } = e`

A destructured equation is one equation per name, each a projection of the
right side that keeps updating; a destructuring `let` is one snapshot local
per name, as in JS. Defaults apply on undefined. Rest is not yet supported.
`[n, setN] = createSignal(0)` keeps its JS_API hint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117BoWP7LmQhib6M9zGnmBH
Bare `x = e` is always an equation, `let x = e` always a snapshot, and only
`:=` and `guess` mutate; destructuring keeps each side's meaning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117BoWP7LmQhib6M9zGnmBH
An object rest is Solid's `omit` of the named keys, a live view (an
internal `omit` expression in every runner and the crate); an array rest is
`.slice(n)`. In a `let`, an object rest is a copy (`{ ...omit(…) }`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117BoWP7LmQhib6M9zGnmBH
@ryansolid
ryansolid merged commit 08255de into main Nov 11, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants