Types phase 3: safety & control flow — option / result + tagged-union enums #53

Closed
opened 2026-08-31 17:30:50 +02:00 by orkun · 1 comment
Owner

Follow-up to #1 (proposal), phase 3 of the phasing.

option — 'no target' / 'empty slot' without magic -1 sentinels (Rust/Swift; C# nullable).

result<T, E> — fallible ops (load, parse, net) without out-params or silent failure. Note the language already has try EXPR else { ... } (#46) recovering a fallible result as a value; this phase generalises that into a first-class result type.

tagged-union enums (payloads) — model game state, events and tile kinds as one value and match on it. Today enum is only int constants; this adds per-variant payloads and exhaustive matching (Rust/Swift; C# records + pattern matching).

Phase 1 (IVec2 + Rect) shipped in the #1 thread.

Follow-up to #1 (proposal), phase 3 of the phasing. **option<T>** — 'no target' / 'empty slot' without magic `-1` sentinels (Rust/Swift; C# nullable). **result<T, E>** — fallible ops (load, parse, net) without out-params or silent failure. Note the language already has `try EXPR else { ... }` (#46) recovering a fallible result as a value; this phase generalises that into a first-class `result` type. **tagged-union enums (payloads)** — model game state, events and tile kinds as one value and `match` on it. Today `enum` is only int constants; this adds per-variant payloads and exhaustive matching (Rust/Swift; C# records + pattern matching). Phase 1 (IVec2 + Rect) shipped in the #1 thread.
orkun added the
proposal
priority:medium
area:types
labels 2026-08-31 17:30:50 +02:00
Author
Owner

Shipped — option, completing the option/result safety pair (790eda6)

Types phase 3 is about safety and clean control flow. Its two concrete sum types are now both in:

  • result / ok / err / try / is_ok / is_err — already shipped in #46. A fallible function returns a result; the caller recovers a value with try EXPR else { … } (the failure message binds to error).
  • option — new here. The "maybe a value" companion, in the same idiom:
    • some(v) — wrap a present value (any i32-width scalar: int/fixed/bool/entity)
    • none() — the empty case, a missing value with no magic -1 sentinel
    • is_some(o) / is_none(o) — test presence
    • unwrap_or(o, fallback) — read the value, or a fallback when empty

Represented as a heap %Option = { i32 present, i32 value }, as bare builtins guarded by find_fn (a user function of the same name still wins) and gated by g_uses_option, so a program that does not use them compiles byte-identically — the same additive design as result.

Tagged-union enums → #56

The third item, tagged-union enums with payloads and exhaustive match, is the one genuinely deep type-system feature in the proposal, and the project's own #46 changeset already flagged it as "sequenced after the tagged-union type system". It cannot land as an additive namespace/builtin: it needs parser support for variant payloads and constructor syntax, a tagged value ABI, match-arm payload binding, and an exhaustiveness/checking pass (Ludic has no validation pass yet). Bundling that invasive change here would risk the bootstrap fixpoint and every golden render. I have split it into its own tracked issue, #56, exactly as the oversized remainder of #1 was decomposed.

Interim coverage for tagged data: the Value.* tree (#44) already models dynamic tagged values (a tag + heterogeneous kids) with a runtime Value.kind match, and enum + match covers the int-tag state-machine case.

Wired end to end: emit_call codegen + %Option decl (emit_decl) + g_uses_option (emit_core), reseeded C-free seed, full vocabulary sync (header / JetBrains / TextMate), per-builtin docs + inventory, and a self-asserting example (examples/library/optionresult.ludic, 12 assertions exercising both option and result, in the regression suite). All suites green: x test 85/85, x selfhost-test 30/30 (golden renders byte-identical, bootstrap fixpoint holds), x test-tools 30/30, check-impl/check-vocabulary/check-docs.

Closing phase 3 (option + result delivered); tagged-union enums continue in #56.

## Shipped — `option`, completing the option/result safety pair (790eda6) Types phase 3 is about safety and clean control flow. Its two concrete sum types are now both in: - **`result` / `ok` / `err` / `try` / `is_ok` / `is_err`** — already shipped in #46. A fallible function returns a `result`; the caller recovers a value with `try EXPR else { … }` (the failure message binds to `error`). - **`option` — new here.** The "maybe a value" companion, in the same idiom: - `some(v)` — wrap a present value (any i32-width scalar: int/fixed/bool/entity) - `none()` — the empty case, a missing value with **no magic `-1` sentinel** - `is_some(o)` / `is_none(o)` — test presence - `unwrap_or(o, fallback)` — read the value, or a fallback when empty Represented as a heap `%Option = { i32 present, i32 value }`, as bare builtins guarded by `find_fn` (a user function of the same name still wins) and gated by `g_uses_option`, so a program that does not use them compiles **byte-identically** — the same additive design as `result`. ### Tagged-union enums → #56 The third item, **tagged-union `enum`s with payloads and exhaustive `match`**, is the one genuinely deep type-system feature in the proposal, and the project's own #46 changeset already flagged it as "sequenced after the tagged-union type system". It cannot land as an additive namespace/builtin: it needs parser support for variant payloads and constructor syntax, a tagged value ABI, `match`-arm payload binding, and an exhaustiveness/checking pass (Ludic has no validation pass yet). Bundling that invasive change here would risk the bootstrap fixpoint and every golden render. I have split it into its own tracked issue, **#56**, exactly as the oversized remainder of #1 was decomposed. Interim coverage for tagged data: the `Value.*` tree (#44) already models dynamic tagged values (a tag + heterogeneous kids) with a runtime `Value.kind` match, and `enum` + `match` covers the int-tag state-machine case. Wired end to end: `emit_call` codegen + `%Option` decl (`emit_decl`) + `g_uses_option` (`emit_core`), reseeded C-free seed, full vocabulary sync (header / JetBrains / TextMate), per-builtin docs + inventory, and a self-asserting example (`examples/library/optionresult.ludic`, 12 assertions exercising both option and result, in the regression suite). All suites green: `x test` 85/85, `x selfhost-test` 30/30 (golden renders byte-identical, bootstrap fixpoint holds), `x test-tools` 30/30, `check-impl`/`check-vocabulary`/`check-docs`. Closing phase 3 (option + result delivered); tagged-union enums continue in #56.
orkun closed this issue 2026-08-31 18:11:48 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: workshopsoft/ludic#53
No description provided.