From 4260a4732c7e86dca9a9c66d6466580e57f828c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 23 Sep 2026 19:51:19 +0000 Subject: [PATCH] :memo: Document why open universes are rejected Add a README Caveats subsection with user-facing guidance for the two open-universe shapes: parse external input at the boundary down to a finite union, or use a runtime Map registry for extensible domains. Point the finite-universe bullet at it, and point the rejected fix/open-universe-* entry in development/library.md back at the new guidance, so rule and decision cross-reference without duplicating each other. --- README.md | 13 +++++++++++++ development/library.md | 4 +++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e09f3e8..c6fb26e 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,19 @@ Yet to be implemented - **`NaN` and `-0` cannot be matched specifically.** They have no literal type, so both stay part of `number`. +### Why open universes are rejected + +An open universe — one carrying a broad member, as in +`type Units = "s" | "ms" | "min" | (string & {})` — is not a dispatch concern. +If the values arrive from outside the program, parse them at the boundary down +to a finite union and match the narrowed result; the openness never reaches the +matcher. If the domain is genuinely extensible, the right shape is a runtime +`Map` of handlers, where "no handler" is a lookup, not a pattern. Either way an +open matcher would abandon the one guarantee this library exists to give — +provable exhaustiveness — to automate what a `switch` and a default arm already +cover. The type-level cost of supporting open universes is recorded in +[development/library.md](./development/library.md#supported-universes). + ## License MIT © 2025 tmu. See [LICENSE](./LICENSE). diff --git a/development/library.md b/development/library.md index 3254d4a..05a7eb9 100644 --- a/development/library.md +++ b/development/library.md @@ -167,7 +167,9 @@ Extract>` catches numeric collisions too. non-self-referential parameter type (`{ [K in keyof H]: … R … }` gives `unknown`); an F-bounded guard referencing `keyof Handled` in `Handled`'s own constraint sees the constraint, not the map; all handlers share one `R` (only - the fallback widens it); `IsLiteral` is the finite-literal predicate. + the fallback widens it); `IsLiteral` is the finite-literal predicate. The + user-facing consequence — open universes are a parsing or registry concern, + not a dispatch one — is guidance in README § Why open universes are rejected. - **`Member` without the gate:** sound, but a colliding handler gets a union and `1 | "1"` stays one runtime key. - **A round-trip injectivity gate** (`IsEqual>>`):