This library is in flux. APIs may change without notice. Generated from source with koruc 0.1.7 on 11/4/2026.
Control
import std/controlKoru Standard Library: Control Flow
control.kz · 5 tors · ~[comptime]
~[keyword] pub tor if { expr: Expression }
| then
| ?else~[keyword] pub tor cond { expr: Expression }
| c *// - `keep:` is OPTIONAL and turns the loop into one that STOPS: the emitted body
// breaks the moment the expression goes false. Omit it and `for` emits
// byte-identical code to before.
//
// This started life as a separate `scan` keyword and that was WRONG. The two
// templates differed by ONE line and shared everything else, across two targets
// — four procs where two do. That is the two-lowerings-of-one-construct shape
// this repo keeps getting bitten by (`frag-a-fix-lands-in-one-lowering-path`),
// installed deliberately by someone who had written that concept's newest
// section the same morning. A second copy that merely agrees today is the same
// bug waiting: the next person to improve the JS range handling would have had
// to remember there were two homes for it. Lars caught it; converging is the
// concept's own prescription.
~[keyword] pub tor for { expr: Expression, keep: ?Expression }
! ?each *
! ?reverse-each *
! ?else *
| ?done~part targets koru_std/control.targets.kz
Koru Standard Library: the `targets` part of control.kz. Joined via `~part targets` in control.kz; merged into the control module at load. The NON-zig template bodies of control.kz's three keywords — the |js and |cs variants of if, cond, for. They share one home because their surfaces converge: if/cond bodies are byte-identical across hosts (the js comment below already argued "shared, not re-derived"), and a fix that lands in one lowering path has to land in all of them — one file is where a sweep actually sees that. The tor decls and the |zig originals stay in control.kz. JS variant of if. The per-call render is target-blind to the EMITTER (the body is spliced inline before variant selection), so the RENDERER selects THIS variant when --lang=js. The body is byte-identical to the Zig one — JS `if/else` shares the same surface — but it is now HONESTLY the JS variant, not a Zig-body leak that happened to also be valid JS. The `__koru_continue_N` markers it mints are resolved by js_emitter's emitInlineBodyResolvingContinuations.
~part zip koru_std/control.zip.kz
Koru Standard Library: the `zip` part of control.kz. Joined via `~part zip` in control.kz; merged into the control module at load. The tor's declaration lives here too — the whole construct is one file (control.kz's line budget leaves no room for a second producer's declaration prose). `zip` walks TWO iterables in lockstep and delivers each aligned pair as a `{ first, second }` record through `! each` — a second producer, not a `for` axis (a `for` call is one traversal of one iterable; the axis refusal pins that). Each side is classified independently: a `lo..hi` range walks by arithmetic, anything else indexes by position, so `zip(0..5, names)` mixes the two freely. The walk stops at the shorter input; `| done n` delivers the pair count so the unpaired tail is an audible number. `step:` strides the pair index — `step: 2` delivers elements 0, 2, 4 of each side. The `! each` payload is `*`-wildcard like `for`'s: the pair is a record, and its field vocabulary is fixed by the emitted literal — `first` (element of `a`), `second` (element of `b`).
// Koru Standard Library: the `zip` part of control.kz.
// Joined via `~part zip` in control.kz; merged into the control module at
// load. The tor's declaration lives here too — the whole construct is one
// file (control.kz's line budget leaves no room for a second producer's
// declaration prose).
//
// `zip` walks TWO iterables in lockstep and delivers each aligned pair as a
// `{ first, second }` record through `! each` — a second producer, not a `for`
// axis (a `for` call is one traversal of one iterable; the axis refusal pins
// that). Each side is classified independently: a `lo..hi` range walks by
// arithmetic, anything else indexes by position, so `zip(0..5, names)` mixes
// the two freely. The walk stops at the shorter input; `| done n` delivers
// the pair count so the unpaired tail is an audible number. `step:` strides
// the pair index — `step: 2` delivers elements 0, 2, 4 of each side. The
// `! each` payload is `*`-wildcard like `for`'s: the pair is a record, and
// its field vocabulary is fixed by the emitted literal — `first` (element of
// `a`), `second` (element of `b`).
~pub tor zip { a: Expression, b: Expression, step: ?Expression }
! ?each *
| ?done usize~part capture-preamble koru_std/control.capture-preamble.kz
Koru Standard Library: the `capture-preamble` part of control.kz. Joined via `~part capture-preamble` in control.kz; merged into the control module at load. Parts compile in their own scope — `struct_literal` is imported here, not inherited from the parent's file. This is the one region of the capture transform that cannot be target-neutral: a capture cell is a mutable named-field record plus a binding, and each host spells that differently. `cellPreamble` returns the whole host-side declaration text for the cell — seed literal and preamble line both — read `host_is_*` once in the caller, spell here.
~part capture koru_std/control.capture.kz
Koru Standard Library: the `capture` part of control.kz. Joined via `~part capture` in control.kz; merged into the control module at load. The cell-declaration spellings (host preamble + seed literal — the one part of the transform that cannot be target-neutral) live in control.capture-preamble.kz — parts are flat, so `~part capture-preamble` is declared in control.kz and the file lands beside this one. CAPTURE - Accumulation with pure semantics (ZERO OVERHEAD) Syntax: capture { total: 0 } ! as acc |> for(items) ! each i |> captured { total: acc.total + i } | captured final |> result(sum: final.total) Both `capture` and `captured` take a SOURCE BLOCK — bare braces, no parens. Bare `name { ... }` is unambiguously an invocation since the `=>` construct glyph took over branch construction (210_047). The parens forms `capture({...})` / `captured({...})` are retired (ratified 2026-06-11). A block holding a single bare expression (`capture { entity }`) seeds from an existing value instead of building a new struct cell. ARCHITECTURE: - capture is a comptime transform: `! as` binds the cell (live/during), `captured { ... }` invocations are rewritten into general `.assignment` nodes, `| captured` is the one-shot after-read. SEMANTICS: - Pure from Koru's perspective (captured computes the NEXT value) - Efficient mutation in generated Zig (var cell + field writes) - Decomposes fold while preserving purity guarantees The tor declares the USER surface (second [transform]proc migrant after std/regex:match): a source block, the `! as` cell effect, the optional `| captured` after-read. The shape checker enforces this contract with zero capture-specific code. The machine interface (invocation/item/program/ allocator → transformed SiteResult) is the proc-return convention.
~[keyword|comptime|transform] pub tor capture {
source: Source,
reporter: std/compiler:*ErrorReporter
}
! as *
| ?captured *