From 1839213df1b404cee8b1077d270d7dc9bfb9edeb Mon Sep 17 00:00:00 2001 From: "shoney.arickathil" Date: Thu, 20 Aug 2026 14:21:58 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20iteration=2020=20=E2=80=94=20Float=20+?= =?UTF-8?q?=20Bytes,=20the=20missing=20scalars=20(forks=20settled)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - brainstorm settled four forks: Float FULL STACK in one iteration (literal, IEEE f64 VM ops via u64 bitcast, @table column + WAL slot, json fidelity — json.c's own comment names the hole: fractions are malformed because "the language has no [float]"); IEEE-754 QUIET semantics (division never traps, NaN flows; Int keeps DIV0; no implicit mixing — float(i)/trunc(f) bridges); BYTES ships alongside (binary carrier: multipart files, WS frames for 19, crypto digests; Text goes back to meaning text); iteration 20 + spec before code (.wob/WAL version bump earns a written spec) - the rest of the missing-type survey recorded with reasons: Result/ Option expressible today (?T + payload unions), tuples covered by records + doctrine, Decimal stays cents-until-a-workload, Char/ Unicode its own future story, Set/ADTs parked post-12, scalar newtypes need the rejected `abstract` - one indexed-storage deviation from raw IEEE spec'd loudly: a Float index needs a total order — NaN sorts last - board row, story-table row (seq 26), graph node (9+16 -> 20 -> 19; crypto gate wants Bytes) Co-Authored-By: Claude Opus 5 (1M context) --- docs/00-code-review.md | 7 ++ docs/00-dependency-graph.md | 2 +- .../language-runtime-database/00-story.md | 1 + .../20-missing-scalar-types.md | 95 +++++++++++++++++++ 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 docs/stories/language-runtime-database/20-missing-scalar-types.md diff --git a/docs/00-code-review.md b/docs/00-code-review.md index 5252db0..abb586a 100644 --- a/docs/00-code-review.md +++ b/docs/00-code-review.md @@ -6,3 +6,10 @@ log-watcher sample compiles. Its findings were extracted on 2026-08-10 into and the plans it amends; its Phase 1–4 roadmap is retired in favour of the approved story iterations. See [`00-status.md`](00-status.md) for current status. + +Native speed — the big one. Everything is interpreted: ~40× behind Go on raw compute, no JIT, no AOT-to-native. The scheduling primitives win benchmarks; a compute-bound handler loses them all back. There is also no Float type at all (the storefront prices in cents for a reason), no SIMD story, and fixed interpreter ceilings (4096 register slots, 256 frames, ~42 KiB per fiber until growable contexts land). + +- Language expressiveness. No generics — the cache stores Text and tells you to json.encode; no function values or closures (doctrine, but it's why every handler is a class with one method); byte-based strings with no Unicode awareness; no Result-style error values (traps + try only); pattern matching is a switch, not destructuring. Some of this is deliberate rejection, but "deliberate" doesn't make the expressiveness appear. +- Concurrency holes the arc hasn't closed. send is one-way — no reply/request-response primitive (my own benchmarks couldn't await the actor and had to sleep); no supervision, links, or actor death (actors live until process end); unbounded mailboxes with zero backpressure; no timers beyond sleep; round-robin placement with no work stealing; multi-shard DB access still traps (stage 3 unbuilt); accept lives on one shard. +- Production plumbing. No TLS anywhere (proxy-mandated forever), no HTTP/2 or WebSockets yet, no crypto primitives (blocked on the bit-ops-vs-builtins fork), observability is print/stderr — no metrics, tracing, or profiler; no debugger, no LSP (discussed, never built); deps are git-rev-only with no registry, no transitive resolution, no semver; blue-green deploy and schema migrations are recorded futures, not features. +- Proof maturity. 9e's benchmark battery has never run — every number so far is a scratch measurement on one machine; TSan covers one demo; no fuzzing, no CI beyond local just, and the whole ecosystem is one framework, five samples, and one committed consumer. The honest summary: the architecture is ahead of the product — the doctrine bets (ownership+inference, actors, one binary, io_uring) are landing and measurable, while the surface a developer touches daily (types, tooling, ecosystem) is years behind the languages it benchmarks against. diff --git a/docs/00-dependency-graph.md b/docs/00-dependency-graph.md index 8daac0b..9ab8fd4 100644 --- a/docs/00-dependency-graph.md +++ b/docs/00-dependency-graph.md @@ -164,7 +164,7 @@ flowchart TD UNIX["unix socket binding"]:::blocked PEERV["trusted-proxy PEER verification"]:::blocked - CRYPTO["GATE: crypto fork — C builtins vs language bit ops (brainstorm)"]:::gate + CRYPTO["GATE: crypto fork — C builtins vs language bit ops (brainstorm); digests want iteration 20's Bytes"]:::gate SHA["SHA-256/512, HMAC, CRC32"]:::blocked ETAG["ETag + conditional requests"]:::blocked COOKIE["signed cookies"]:::blocked diff --git a/docs/stories/language-runtime-database/00-story.md b/docs/stories/language-runtime-database/00-story.md index 676bf94..fce25a2 100644 --- a/docs/stories/language-runtime-database/00-story.md +++ b/docs/stories/language-runtime-database/00-story.md @@ -72,6 +72,7 @@ iterations by number, so numbers never renumber. | 22 | 9g | [Query grammar corpus](refine/09g-query-grammar-corpus.md) | grow the query grammar from real corpora; likely collapses to "confirm `len(query)` + add `exists`"; precedes 14 | | 23 | 14 | [skillhost host workload](refine/14-skillhost-host-workload.md) | host-shaped driving workload naming runtime gaps (bounded subprocess, stdin/stdout transport, fs metadata, FFI-vs-out-of-process) — demoted with the framework goal | | 24 | 13 | [Compile-time metaprogramming](refine/13-compile-time-metaprogramming.md) | `@derive(Json/Csv/Eq/Hash/Show)` from class-table metadata; held behind 12 with the parked drain by the 2026-08-08 scope directive | +| 26 | 20 | [Float + Bytes](20-missing-scalar-types.md) | the missing scalars, full stack: IEEE-quiet f64 through literals/VM/@table/WAL/json (fractions decode at last) + Bytes as the binary carrier; forks settled 2026-08-20, spec next — feeds 19 (WS frames) and the crypto fork (digests) | | ⏸ | 17 | [library projects + `internal/`](17-library-projects-internal.md) | **PARKED** (spec + plan approved, branch `library-internal`) — `wo.toml` kind = "library" + Go's `internal/` rule; slots anywhere after 16 whenever directed, bringing the framework reorg with it | Review protocol: the developer reads one iteration, approves or amends; diff --git a/docs/stories/language-runtime-database/20-missing-scalar-types.md b/docs/stories/language-runtime-database/20-missing-scalar-types.md new file mode 100644 index 0000000..d5ae3d9 --- /dev/null +++ b/docs/stories/language-runtime-database/20-missing-scalar-types.md @@ -0,0 +1,95 @@ +# Iteration 20 — the missing scalar types: Float and Bytes + +> Format: fiberloom `product/story-iteration-template`. Part of +> [Story — one language, one runtime, one database, one binary](00-story.md). +> +> **Inserted 2026-08-20, forks settled the same day** (developer +> decisions below). Next: spec, then plan — a new storage kind touches +> the `.wob`/WAL formats, so this one earns its written spec. + +## Why this iteration exists + +The scalar inventory is `Int`, `Bool`, `Text` (+`Timestamp`/`Id`, +Int-shaped conveniences) — and `runtime/src/json.c` says the quiet part +out loud: "a fraction or an exponent is MALFORMED for an Int field (the +language has no [float])". Any JSON body containing `1.5` fails decode +today; a web framework that cannot accept `{"price": 9.99}` has a hole, +not a style. Separately, binary payloads (multipart file parts, the +coming WebSocket frames, crypto digests) ride Text-as-bytes — proven to +work, but it blurs every json/interpolation boundary Text has. + +## Settled decisions (2026-08-20) + +1. **Float, full stack in one iteration**: literal lexing, f64 + arithmetic, an `@table` column kind + WAL slot, json decode/encode + fidelity (the forcing function), interpolation, `parse_float`. A + language-only Float would leave the visible blocker standing. +2. **IEEE 754 quiet semantics, exactly**: Float division never traps + (Inf/NaN flow), `NaN != NaN`, shortest-round-trip printing. Int keeps + its DIV0 trap — two numeric worlds, NO implicit mixing: `float(i)` + and `trunc(f)` are the explicit bridges. +3. **Bytes ships alongside**: a distinct binary scalar (len/byte_at/ + slice/compare; base64 and future digests return it; net reads can + fill it) — Text goes back to meaning text; iteration 19 and the + crypto fork inherit a clean carrier. +4. **Recorded as iteration 20; spec before code.** + +## Surveyed and deliberately NOT added (the rest of the missing-type list) + +- **Result/Option** — expressible TODAY: `?T` plus payload unions + (`type R = Ok(v: Int) | Err(msg: Text)`); nothing to add. +- **Tuples** — records cover them; "no tuples" is standing doctrine. +- **Decimal/Money** — cents-as-Int holds until a workload proves + otherwise; recorded, not scheduled. +- **Char/Rune + Unicode-aware Text** — its own future story; + `byte_at`/`char_of` carry today's workloads. +- **Set and the ADT roster** — parked post-12 by the 2026-08-08 scope + directive, unchanged. +- **Scalar newtypes** (a real Duration/Timestamp distinct from Int) — + needs `abstract`, which is a reject row; the ms-Int convention stays + documented instead. + +## Goals (draft — the spec refines) + +- `1.5`, `-0.25`, `2e10` lex as Float literals; arithmetic/comparison + lower to IEEE f64 ops (registers are u64 — bitcast, no layout change). +- A `Float` field is a real `@table` column: stored, WAL-logged, + replayed, indexed comparisons defined (total order with NaN sorted + last, the one deviation from raw IEEE that an index REQUIRES — + spec'd loudly). +- `json.decode` accepts fractions/exponents into Float fields and + `json.encode` round-trips shortest-form; Int fields still reject + fractions (unchanged strictness). +- `Bytes`: literal-less (built from base64/net/slices), length-carrying, + content-comparable; `@table` column + WAL; json boundary = base64 + string by convention (spec'd). +- The web-app grows a Float column (a real price) as the living proof. + +## Acceptance Criteria (draft) + +- **Given** `{"price": 9.99}` posted to the storefront, **when** the + Float-column model decodes it, **then** insert/query/order-by work and + the value round-trips json byte-identically. +- **Given** Float edge values (NaN, Inf, -0.0, 1e308), **when** they + flow through arithmetic, storage, replay, and json, **then** IEEE + semantics hold end-to-end and the WAL replays them bit-exact. +- **Given** a Bytes value from base64_decode, **when** it is stored, + compared, sliced, and re-encoded, **then** content survives + bit-exact and no Text builtin silently accepts it (or vice versa) + where the spec says types differ. +- Every standing gate stays green; `.wob`/WAL format changes are + version-bumped and documented in the format doc in the same change. + +## Out Of Scope + +f32; SIMD; Decimal; numeric literals with underscores; implicit +Int↔Float coercion anywhere (including `==` across the divide); +Unicode; hashing/ordering of Bytes in maps beyond equality (the map key +story stays Int/Text v1). + +## Proposed Solution + +Spec next: the two kinds' `.wob`/WAL encoding (format version bump), +the IEEE-with-indexed-NaN ordering rule, the json boundaries, the +builtin surface (`float`, `trunc`, `parse_float`, Bytes accessors), and +the web-app price-column proof as the gate. Plan follows the spec.