52 lines
2.6 KiB
Markdown
52 lines
2.6 KiB
Markdown
|
|
# NullBox / MissingBox — Design and Rollout (Observe → Adopt)
|
||
|
|
|
||
|
|
Status: proposal accepted for dev-only observation; defaults unchanged
|
||
|
|
|
||
|
|
## Goals
|
||
|
|
|
||
|
|
- Make “absence of value” explicit and observable without changing prod behavior.
|
||
|
|
- Separate “explicit null” from “missing/unset” to improve diagnostics, reduce ambiguity, and simplify downstream policies.
|
||
|
|
- Stage the rollout via Observe → Adopt: enable metrics in dev, verify zero-diff on outputs, then adopt selectively.
|
||
|
|
|
||
|
|
## Terms
|
||
|
|
|
||
|
|
- Null = explicit no-value. Represented by NullBox (stringify: "null").
|
||
|
|
- Missing = absent/unset (missing key, uninitialized). Represented by MissingBox (stringify: "(missing)" in dev only; not surfaced in prod).
|
||
|
|
- Bottom/void (unreachable/side-effect-only) is NOT materialized as a box. Using it as a value is a bug (trap).
|
||
|
|
|
||
|
|
## Behavior (policy sketch)
|
||
|
|
|
||
|
|
- Equality: Null == Null → true. Missing: comparison is error.
|
||
|
|
- Ordering (<, <=, >, >=): Null and Missing → error.
|
||
|
|
- Arithmetic: Null propagates (returns Null) or errors when `NYASH_NULL_STRICT=1`. Missing → error.
|
||
|
|
- Coalesce `??`: Null ?? x = x, Missing ?? x = x.
|
||
|
|
- Safe-call `?.`: Null?.m() = Null; Missing?.m() = Missing (or error by policy).
|
||
|
|
|
||
|
|
Note: language operators `??`/`?.` may be introduced later; initial staging can use functions or Operator Boxes.
|
||
|
|
|
||
|
|
## Implementation plan (minimal, reversible)
|
||
|
|
|
||
|
|
1) Types (done, dev scope only)
|
||
|
|
- Add `NullBox` (already present) and `MissingBox` (new) as first-class boxes.
|
||
|
|
- No change to default value mapping: VM maps NullBox to VMValue::Void for backward compatibility.
|
||
|
|
|
||
|
|
2) Env toggles
|
||
|
|
- `NYASH_NULL_MISSING_BOX=1`: enable observation path (no default behavior changes).
|
||
|
|
- `NYASH_NULL_STRICT=1`: strict policy (operators error on null) — effective only when the first flag is enabled.
|
||
|
|
|
||
|
|
3) VM integration (dev-only observation)
|
||
|
|
- Classification helpers recognize BoxRef(NullBox/MissingBox) for traces.
|
||
|
|
- Print: Void and BoxRef(VoidBox) → "null"; BoxRef(NullBox) prints via box `toString` ("null"); MissingBox prints "(missing)" in dev.
|
||
|
|
- Compare/Add: no default behavior changes; strict behavior is gated behind envs and will be staged later.
|
||
|
|
|
||
|
|
4) JSON/Node integration (later)
|
||
|
|
- Optional: when flag is on, `object_get/array_get` may return `MissingBox` for absent entries, while `null` remains `NullBox`.
|
||
|
|
- UI/print normalization can translate Missing → null at boundaries based on policy.
|
||
|
|
|
||
|
|
## Acceptance
|
||
|
|
|
||
|
|
- Defaults unchanged (prod): no output or semantic differences.
|
||
|
|
- Dev-only path provides metrics and safe diagnostics; quick/integration smokes remain green.
|
||
|
|
- After stability, selective adopt in Compare/Add can be enabled (Compare already adopted by default).
|
||
|
|
|