Skip to main content

blue_lang_runtime/
docs.rs

1//! One line for every name a blue program can call without a `use`.
2//!
3//! The interpreter [`crate::interpreter`] builds binds 228 names a blue
4//! call can spell (`blue_lang_syntax::is_callable_name`, measured 2026-09-29).
5//! The reserved words among its bindings (`if`, `fn`, `case`, …) are described
6//! with the keywords, and the kebab-case rest of tatara-lisp's stdlib is
7//! unreachable, because the lexer reads `-` as an operator. Most are installed by tatara-lisp, which records no description,
8//! and the rest by this crate's own layers. This table is where blue says what
9//! each one does, in blue's calling form: `blue reference` prints it and
10//! `docs/REFERENCE.md` is rendered from it.
11//!
12//! **It cannot drift from the runtime.** `blue-lang-cli/tests/reference.rs`
13//! compares it with a live interpreter in both directions: a name the runtime
14//! binds with no row here, a row naming nothing the runtime binds, and a
15//! signature whose parameter count disagrees with the bound function's arity
16//! are each a red build. A new primitive therefore arrives with its line or
17//! does not arrive.
18//!
19//! Every line was checked against the runtime by running it, not read off a
20//! name. Where a builtin is partial or surprising the line says so, because
21//! that is what an author needs from it.
22//!
23//! # Signatures
24//!
25//! `name(a, b)` takes exactly two; `name(a[, b])` takes one or two;
26//! `name(a, more...)` takes one or more. A name that is not a function (a
27//! special form, a macro, a constant) is written the way a blue program uses
28//! it, and its arity is not checked.
29
30/// What a name is for, so a reference can group it.
31#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
32pub enum Topic {
33    /// Evaluation forms: conditionals, bindings, quoting.
34    Form,
35    /// tatara-lisp forms whose arguments are Lisp shapes; usable from blue
36    /// only where the shape is plain values.
37    Lisp,
38    Number,
39    Text,
40    List,
41    Map,
42    /// Functions that take or return functions.
43    Function,
44    /// Questions about a value's kind or sign.
45    Predicate,
46    Error,
47    Json,
48    Nix,
49    Xml,
50    File,
51    Process,
52    Environment,
53    Clock,
54    /// NATS and HTTP over TCP.
55    Network,
56    /// Sound played and captured, and a canvas drawn and shown.
57    Device,
58    Crypto,
59    /// Delayed values.
60    Lazy,
61    /// tatara-lisp's channels and fibers.
62    Concurrency,
63    /// blue's own processes: spawn, mailboxes, links, supervision.
64    Actor,
65    /// Printing to the terminal.
66    Output,
67    /// The master verbs, dispatched on the subject's space.
68    Dispatch,
69}
70
71impl Topic {
72    /// Every topic, in the order a reference presents them.
73    pub const ALL: &'static [Topic] = &[
74        Topic::Form,
75        Topic::Number,
76        Topic::Text,
77        Topic::List,
78        Topic::Map,
79        Topic::Dispatch,
80        Topic::Function,
81        Topic::Predicate,
82        Topic::Error,
83        Topic::Json,
84        Topic::Nix,
85        Topic::Xml,
86        Topic::File,
87        Topic::Process,
88        Topic::Environment,
89        Topic::Clock,
90        Topic::Network,
91        Topic::Device,
92        Topic::Crypto,
93        Topic::Output,
94        Topic::Lazy,
95        Topic::Concurrency,
96        Topic::Actor,
97        Topic::Lisp,
98    ];
99
100    /// The heading a reference gives the topic.
101    #[must_use]
102    pub fn title(self) -> &'static str {
103        match self {
104            Topic::Form => "Forms",
105            Topic::Lisp => "tatara-lisp forms",
106            Topic::Number => "Numbers",
107            Topic::Text => "Strings",
108            Topic::List => "Lists",
109            Topic::Map => "Maps",
110            Topic::Function => "Functions",
111            Topic::Predicate => "Predicates",
112            Topic::Error => "Errors",
113            Topic::Json => "JSON",
114            Topic::Nix => "Nix source",
115            Topic::Xml => "XML",
116            Topic::File => "Files and paths",
117            Topic::Process => "Processes",
118            Topic::Environment => "Environment and arguments",
119            Topic::Clock => "Time",
120            Topic::Network => "Network",
121            Topic::Device => "Sound and display",
122            Topic::Crypto => "Hashing and signatures",
123            Topic::Lazy => "Delayed values",
124            Topic::Concurrency => "Channels and fibers",
125            Topic::Actor => "Blue processes and supervision",
126            Topic::Output => "Terminal output",
127            Topic::Dispatch => "Master verbs",
128        }
129    }
130
131    /// What calling a name of this topic may do besides compute its answer
132    /// from its arguments; `None` when that is all it does. Exhaustive, so a
133    /// new topic decides here. The call memo (`blue-lang-mondou`'s `reach`)
134    /// answers a call from its cache only when nothing the definition reaches
135    /// has an effect.
136    #[must_use]
137    pub fn effect(self) -> Option<Effect> {
138        match self {
139            Topic::Form
140            | Topic::Lisp
141            | Topic::Number
142            | Topic::Text
143            | Topic::List
144            | Topic::Map
145            | Topic::Function
146            | Topic::Predicate
147            | Topic::Error
148            | Topic::Json
149            | Topic::Nix
150            | Topic::Xml
151            | Topic::Crypto
152            | Topic::Lazy => None,
153            Topic::File
154            | Topic::Process
155            | Topic::Environment
156            | Topic::Clock
157            | Topic::Network
158            | Topic::Device => Some(Effect::Host),
159            Topic::Output => Some(Effect::Output),
160            Topic::Concurrency => Some(Effect::Concurrency),
161            Topic::Actor => Some(Effect::Actor),
162            Topic::Dispatch => Some(Effect::Dispatch),
163        }
164    }
165}
166
167/// What a call can do besides answer ([`Topic::effect`]).
168#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
169pub enum Effect {
170    /// Reaches the host: files, processes, the environment, the clock, the
171    /// network (`blue_lang_waku::Capability::host_effects`).
172    Host,
173    /// Writes to the terminal.
174    Output,
175    /// Talks to a channel or a fiber.
176    Concurrency,
177    /// Talks to a blue process.
178    Actor,
179    /// Runs the handler a space registered for a master verb, which the
180    /// program can replace between two equal calls.
181    Dispatch,
182}
183
184impl Effect {
185    #[must_use]
186    pub fn label(self) -> &'static str {
187        match self {
188            Effect::Host => "host",
189            Effect::Output => "output",
190            Effect::Concurrency => "concurrency",
191            Effect::Actor => "process",
192            Effect::Dispatch => "dispatch",
193        }
194    }
195}
196
197/// One bound name, described.
198#[derive(Clone, Copy, Debug)]
199pub struct NameDoc {
200    pub name: &'static str,
201    pub topic: Topic,
202    /// How a blue program calls it. See the module docs for the grammar.
203    pub signature: &'static str,
204    /// One line: what it returns, and the trap in it when there is one.
205    pub doc: &'static str,
206}
207
208/// `names! { topic: "name" "signature" "doc"; … }` — one row per name, so the
209/// table reads as a table.
210macro_rules! names {
211    ($($topic:ident: $name:literal $sig:literal $doc:literal;)*) => {
212        &[$(NameDoc { name: $name, topic: Topic::$topic, signature: $sig, doc: $doc },)*]
213    };
214}
215
216/// Every name the full interpreter binds that a blue identifier can spell.
217pub const NAMES: &[NameDoc] = names! {
218    // ── forms ─────────────────────────────────────────────────────────────
219    Form: "when" "when(c, body...)" "The body's last value when c is truthy, else nil. An if with no else says the same thing.";
220    Form: "define" "define(name, value)" "Bind a name. Written name = value; the formatter prints that form.";
221    Form: "set!" "set!(name, value)" "Rebind an existing name. A name a forked process inherited is sealed and refuses it.";
222    Form: "lambda" "fn(x) … end" "The function form fn lowers to. Write fn.";
223    Form: "quasiquote" "quote … end" "What quote with unquote lowers to inside defmacro.";
224    Form: "try" "try(expr, catch(e(), handler))" "The Lisp form begin … rescue lowers to. Written `expr rescue fallback` or a begin … rescue e … end block (B0037).";
225    Form: "while" "while(c, body)" "Evaluate body while c is truthy; nil. Needs set! to make progress, so map, filter and reduce are usually the better word.";
226    Form: "comment" "comment(anything...)" "Ignores its arguments and is nil. Use # comments instead.";
227    Form: "eval" "eval(form)" "Evaluate a quoted form.";
228    Form: "macroexpand" "macroexpand(form)" "The expansion of a quoted macro call, for debugging a defmacro.";
229    Form: "delay" "delay(expr)" "A promise that evaluates expr once, on the first force.";
230    // ── tatara-lisp forms ─────────────────────────────────────────────────
231    Lisp: "let" "(let ((x 1)) …)" "Lisp binding form; its binding list has no blue spelling. In blue, write x = 1.";
232    Lisp: "letrec" "(letrec ((f …)) …)" "Lisp recursive binding; its binding list has no blue spelling. In blue, use def.";
233    Lisp: "cond" "(cond (test expr) …)" "Lisp multi-way conditional; its clauses have no blue spelling. In blue, use if … elsif … end.";
234    Lisp: "require" "(require …)" "tatara-lisp's module loader. In blue, use(\"pkg\") imports a bidama.";
235    Lisp: "provide" "(provide …)" "tatara-lisp's module export. A bidama exports every def.";
236    Lisp: "dolist" "(dolist (x xs) …)" "Lisp loop; its binding list has no blue spelling. In blue, map(fn(x) … end, xs).";
237    Lisp: "doseq" "(doseq (x xs) …)" "Same as dolist. In blue, map(fn(x) … end, xs).";
238    Lisp: "dotimes" "(dotimes (i n) …)" "Lisp counted loop. In blue, map(fn(i) … end, range(n)).";
239    Lisp: "match" "(match v (pattern expr) …)" "Lisp pattern matching; its patterns have no blue spelling. blue's case compares values and does not destructure.";
240    Lisp: "defflow" "defflow(name, f, g, more...)" "Define name as a function that pipes its argument through f, then g: defflow(slug, trim, downcase).";
241    Lisp: "defsm" "defsm(name, :initial, s, :transitions, [[from, event, to], …])" "Define a state machine. name(:send, event) moves it and returns the state; name(:current) reads it.";
242    Lisp: "defstrategy" "defstrategy(name, :variant, f, …, :default, g)" "Define name(variant, args...) dispatching to the function under that keyword, or :default.";
243    Lisp: "defvisitor" "defvisitor(name, :tag, f, …, :default, g)" "Define name(tagged) dispatching on the first element of a tagged list.";
244    Lisp: "defactor" "defactor(name, initial, behaviour)" "A single-threaded actor with a mailbox. Its step message is :step!, which blue cannot spell, so from blue it can only be told, never stepped.";
245    Lisp: "defcommand" "defcommand(bus, name, params, body)" "Register a command on a tatara-lisp command bus; params is a Lisp list.";
246    Lisp: "defquery" "defquery(bus, name, params, body)" "Register a query on a tatara-lisp command bus; params is a Lisp list.";
247    // ── numbers ───────────────────────────────────────────────────────────
248    Number: "abs" "abs(x)" "Absolute value, keeping Int or Float.";
249    Number: "min" "min(x, more...)" "The smallest argument. Takes numbers, not a list: apply(min, xs).";
250    Number: "max" "max(x, more...)" "The largest argument. Takes numbers, not a list: apply(max, xs).";
251    Number: "inc" "inc(x)" "x + 1.";
252    Number: "dec" "dec(x)" "x - 1.";
253    Number: "floor" "floor(x)" "The largest Int not above x. floor(a / b) is integer division.";
254    Number: "ceiling" "ceiling(x)" "The smallest Int not below x.";
255    Number: "round" "round(x)" "The nearest Int, halves away from zero: round(2.5) is 3, round(-2.5) is -3.";
256    Number: "truncate" "truncate(x)" "The Int part of x, toward zero: truncate(-2.7) is -2.";
257    Number: "mod" "mod(a, b)" "Euclidean remainder, never negative; what % lowers to.";
258    Number: "modulo" "modulo(a, b)" "The same as mod.";
259    Number: "rem" "rem(a, b)" "The same as mod here: mod(-7, 3), modulo and rem are all 2.";
260    Number: "gcd" "gcd(xs...)" "Greatest common divisor of the arguments; 0 for none.";
261    Number: "lcm" "lcm(xs...)" "Least common multiple of the arguments.";
262    Number: "expt" "expt(base, power)" "base raised to power: expt(2, 10) is 1024.";
263    Number: "sqrt" "sqrt(x)" "Square root, always a Float: sqrt(16) is 4.0, and 4.0 == 4.";
264    Number: "exp" "exp(x)" "e to the x, a Float.";
265    Number: "log" "log(x[, base])" "Natural logarithm, or log to base: log(100, 10) is 2.0.";
266    Number: "sin" "sin(x)" "Sine of x radians.";
267    Number: "cos" "cos(x)" "Cosine of x radians.";
268    Number: "tan" "tan(x)" "Tangent of x radians.";
269    Number: "asin" "asin(x)" "Arcsine; refuses x outside [-1, 1] rather than returning NaN.";
270    Number: "acos" "acos(x)" "Arccosine; refuses x outside [-1, 1] rather than returning NaN.";
271    Number: "atan" "atan(x)" "Arctangent in radians.";
272    Number: "atan2" "atan2(y, x)" "The angle of the point (x, y); y comes first.";
273    Number: "hypot" "hypot(x, y)" "sqrt(x * x + y * y) without the overflow: hypot(3, 4) is 5.0.";
274    Number: "to_int" "to_int(x)" "An Int from a string or a number, truncating a Float; nil for text that is not an integer (\"3.7\" too).";
275    Number: "to_int!" "to_int!(x)" "Like to_int, but raises instead of returning nil.";
276    Number: "to_float" "to_float(x)" "A Float from a number or a numeric string.";
277    Number: "compare" "compare(a, b)" "-1, 0 or 1. Total over two numbers or two strings, which < is not; mixing them raises.";
278    // ── strings ───────────────────────────────────────────────────────────
279    Text: "concat" "concat(x, more...)" "The text of every argument joined, each rendered with to_s. What interpolation lowers to.";
280    Text: "to_s" "to_s(x)" "Text for any value (okite D0005, D0006): nil is \"\", 1.0 is \"1.0\", a list or map its blue literal, a keyword its name.";
281    Text: "string" "string(x)" "tatara-lisp's rendering: :a stays \":a\" and a list prints as (1). Use to_s.";
282    Text: "upcase" "upcase(s)" "The string in upper case. Raises on nil.";
283    Text: "downcase" "downcase(s)" "The string in lower case. Raises on nil.";
284    Text: "trim" "trim(s[, opts])" "The string without leading and trailing whitespace. opts: side: :both (default), :start or :end; chars: the characters to cut instead of whitespace. An unknown option raises :bad_option.";
285    Text: "split" "split(s, sep)" "The fields between separators, empty ones kept (okite D0012): split(\"a,b,\", \",\") is [\"a\", \"b\", \"\"]. An empty sep splits into characters.";
286    Text: "join" "join(xs, sep)" "The elements' text with sep between. The LIST comes first here, and the string first in split.";
287    Text: "join_lines" "join_lines(xs)" "The elements' text, one per line, with no newline after the last: what a string with a \\n escape is written as (B0065).";
288    Text: "unlines" "unlines(xs)" "The elements' text, each followed by a newline: text that ends in a line break (B0065).";
289    Text: "line_break" "line_break" "The one-character line-break string, \"\\n\" (B0065). `newline` prints one.";
290    Text: "replace" "replace(s, from, to)" "Every occurrence of from replaced by to.";
291    Text: "chars" "chars(s)" "The characters as one-character strings.";
292    Text: "starts_with?" "starts_with?(s, prefix)" "Whether s begins with prefix.";
293    Text: "ends_with?" "ends_with?(s, suffix)" "Whether s ends with suffix.";
294    Text: "contains?" "contains?(s, sub)" "Whether the STRING s contains sub. On a list it raises: use member?(x, xs).";
295    Text: "text_index" "text_index(s, sub, from)" "The character index of the first sub in s starting at or after from, or -1. One linear pass; moji's char_index wraps it.";
296    Text: "text_last_index" "text_last_index(s, sub, from)" "The character index of the last sub in s starting at or before from, or -1. One linear pass.";
297    Text: "text_bytes" "text_bytes(s)" "The UTF-8 bytes of a string, as a list of integers 0..255: text_bytes(\"é\") is [195, 169].";
298    Text: "bytes_text" "bytes_text(bytes)" "The string whose UTF-8 bytes these are (a byte list or a string); nil when they are not UTF-8. An element outside 0..255 raises.";
299    // ── lists ─────────────────────────────────────────────────────────────
300    List: "list" "list(xs...)" "A list of the arguments. Written [a, b].";
301    List: "length" "length(seq)" "The length of a list or string. Raises on nil: retsu's size(xs) is total.";
302    List: "count" "count(x)" "The number of elements; 0 for nil.";
303    List: "nth" "nth(i, xs)" "The element at index i, from 0; nil past either end. The INDEX comes first.";
304    List: "first" "first(xs)" "The first element; raises on []. retsu's first answers nil.";
305    List: "second" "second(xs)" "The second element; raises when there is none.";
306    List: "third" "third(xs)" "The third element; raises when there is none.";
307    List: "fourth" "fourth(xs)" "The fourth element; raises when there is none.";
308    List: "last" "last(xs)" "The last element; raises on []. retsu's last answers nil.";
309    List: "rest" "rest(xs)" "All but the first; raises on []. retsu's rest answers [].";
310    List: "next" "next(xs)" "All but the first; raises on [].";
311    List: "butlast" "butlast(xs)" "All but the last; raises on [].";
312    List: "car" "car(xs)" "The first element. Prefer first.";
313    List: "cdr" "cdr(xs)" "All but the first ([] for a one-element list); raises on []. Prefer rest.";
314    List: "cons" "cons(x, tail)" "A new list with x in front. Shares tail rather than copying it, and is in place when tail is held nowhere else.";
315    List: "append" "append(lists...)" "The lists joined, in order; nil counts as []. Persistent: O(log n), sharing structure with the operands, and in place when the first list is held nowhere else (an accumulator in a tail call), so building a list one element at a time is linear.";
316    List: "take" "take(n, xs)" "The first n elements, or all of them. The COUNT comes first.";
317    List: "drop" "drop(n, xs)" "All but the first n. The COUNT comes first.";
318    List: "reverse" "reverse(seq)" "The list or string backwards. Raises on nil.";
319    List: "range" "range(a[, b][, step])" "Ints from a up to but not including b by step; range(n) is 0 to n - 1. Native, so any length is safe.";
320    List: "flatten" "flatten(xs)" "Every nested list spliced in, to any depth.";
321    List: "distinct" "distinct(xs)" "The elements with repeats removed, first occurrence kept, compared with ==.";
322    List: "frequencies" "frequencies(xs)" "A map from each element to how many times it occurs.";
323    List: "zip" "zip(xs, ys)" "[[x1, y1], [x2, y2], …], as long as the shorter list.";
324    List: "interleave" "interleave(xs, ys)" "[x1, y1, x2, y2, …], as long as the shorter list allows.";
325    List: "intersperse" "intersperse(sep, xs)" "xs with sep between each pair. The SEPARATOR comes first.";
326    List: "position" "position(x, xs)" "The index of the first element == x, or -1.";
327    List: "member?" "member?(x, xs)" "Whether some element == x. The ELEMENT comes first.";
328    List: "partition" "partition(f, xs)" "[kept, rejected]: the elements f accepts, then the rest.";
329    List: "sort_keyed" "sort_keyed(key, xs)" "A stable sort by key(x), under compare's rules; native, so safe at any length. Mixed key kinds raise.";
330    List: "iterate" "iterate(f, x, n)" "[x, f(x), f(f(x)), …], n elements.";
331    List: "repeatedly" "repeatedly(f, n)" "A list of n calls to the zero-argument function f. The FUNCTION comes first.";
332    // ── maps ──────────────────────────────────────────────────────────────
333    Map: "get" "get(m, k)" "The value at key k, or nil. Raises on nil and on a list.";
334    Map: "assoc" "assoc(m, k, v)" "A new map with k set to v; m is unchanged. Persistent: O(log n), and in place when m is held nowhere else (an accumulator in a tail call). Raises on nil.";
335    Map: "dissoc" "dissoc(m, k)" "A new map without k; unchanged when k is absent.";
336    Dispatch: "verb_for" "verb_for(space, verb, f)" "Register f as the handler of a master verb for a space. The verb is one of read, need, has?, put, update, delete, create, diff, info, exists?; another name raises :usage. Registering again replaces. Spaces call it at the top of their bidama.";
337    Dispatch: "space_for" "space_for(kind, space)" "Name the space that owns untagged values of a kind: :map, :list, :string, :int, :float, :bool, :keyword or :function. A kind already owned by another space raises :usage.";
338    Dispatch: "in_space" "in_space(space, value)" "The value as a subject of a space: {space: s, of: value}. Master verbs call the space's handler with the value itself. A value already in another space raises :usage.";
339    Dispatch: "space_of" "space_of(value)" "The space a master verb would dispatch the value to (its tag, else the owner of its kind), or nil.";
340    Dispatch: "read" "read(source, more...)" "The whole content of a source, by the source's space: in_space(\"gitto\", repo) reads a file at a ref. Raises :usage when no space owns the subject or the space has no handler.";
341    Dispatch: "need" "need(store, key, more...)" "One entry, raising :not_found when absent. A plain map goes to jisho.";
342    Dispatch: "has?" "has?(store, key, more...)" "Whether the key is present. A plain map goes to jisho.";
343    Dispatch: "put" "put(store, key, value, more...)" "Create or replace an entry; a plain map goes to jisho, which returns a new map.";
344    Dispatch: "update" "update(store, key, f, more...)" "Replace an entry with f of the old value; raises :not_found when absent. A plain map goes to jisho.";
345    Dispatch: "delete" "delete(store, key, more...)" "Make an entry absent; absent counts as done. A plain map goes to jisho; in_space(\"gitto\", repo) deletes a branch or tag.";
346    Dispatch: "create" "create(store, key, value, more...)" "Add a new entry, refusing to overwrite; in_space(\"gitto\", repo) makes a branch or tag.";
347    Dispatch: "diff" "diff(a, b, more...)" "The differences between two values as change records, by the space of a: diff(in_space(\"gitto\", repo), from, to) compares two refs.";
348    Dispatch: "info" "info(subject, more...)" "The current state of the subject as a record: in_space(\"gitto\", repo) is the repository's state.";
349    Dispatch: "exists?" "exists?(subject, more...)" "Whether the subject is there; in_space(\"gitto\", repo) asks about a ref.";
350    Map: "dig" "dig(tree, keys...)" "The value at the path of keys through nested maps and lists, or nil at the first step that is absent or cannot be entered. A list takes an Int index, negative from the end; dig(tree) is tree.";
351    Map: "bury" "bury(tree, keys..., value)" "A new tree with value at the path of keys, every missing level made as a map. Refuses with :bad_path when a value on the way is neither nil, a map nor a list, so data is never replaced silently; a list index may be one past the end; bury(tree, value) is value.";
352    Map: "zipmap" "zipmap(keys, values)" "A map pairing keys with values, as long as the shorter list.";
353    Map: "map_entries" "map_entries(m)" "Every [key, value] pair of a map, ordered by key under compare's rules; [] for nil. Mixed key kinds raise.";
354    // ── functions ─────────────────────────────────────────────────────────
355    Function: "map" "map(f, xs, more...)" "f applied to each element; with two lists, f(x, y) pairwise. The FUNCTION comes first. nil maps to [].";
356    Function: "filter" "filter(f, xs)" "The elements f accepts. The FUNCTION comes first.";
357    Function: "remove" "remove(f, xs)" "The elements f rejects.";
358    Function: "reduce" "reduce(f, [init, ]xs)" "Fold from the left: reduce(fn(acc, x) … end, 0, xs). Without init, the first element is the start.";
359    Function: "foldl" "foldl(f, init, xs)" "reduce with an init: f(acc, x) from the left.";
360    Function: "foldr" "foldr(f, init, xs)" "Fold from the right: f(x, acc).";
361    Function: "find" "find(xs, f)" "The first element f accepts, or nil. The COLLECTION comes first; nil is the empty list. find(f, xs) is still answered, because tatara-lisp's state machines and buses call it so.";
362    Function: "flat_map" "flat_map(xs, f)" "f applied to each element, the lists it returns joined in order (one level); f may return nil for none. The COLLECTION comes first.";
363    Function: "group_by" "group_by(xs, f)" "A map from f(x) to the list of the elements with that key, each list in order. The key must be a scalar. The COLLECTION comes first.";
364    Function: "some" "some(f, xs)" "true when f accepts an element, else nil (not false).";
365    Function: "any?" "any?(f, xs)" "Whether f accepts some element; false for [].";
366    Function: "every?" "every?(f, xs)" "Whether f accepts every element; true for [].";
367    Function: "apply" "apply(f, args..., xs)" "Call f with the elements of the last list as its arguments. With f a name it is written as a splat, max(*xs) (B0042); apply stays for a function computed by an expression.";
368    Function: "identity" "identity(x)" "x.";
369    Function: "const" "const(x)" "A function that ignores its argument and returns x.";
370    Function: "comp" "comp(f, g)" "The function x -> f(g(x)).";
371    Function: "compose" "compose(fs...)" "Right to left: compose(f, g)(x) is f(g(x)).";
372    Function: "pipe" "pipe(fs...)" "Left to right: pipe(f, g)(x) is g(f(x)).";
373    Function: "partial" "partial(f, args...)" "f with its first arguments fixed: partial(f, 1)(2) is f(1, 2).";
374    Function: "flip" "flip(f)" "f with its two arguments swapped.";
375    Function: "juxt" "juxt(fs...)" "The function x -> [f(x), g(x), …].";
376    Function: "tap" "tap(f, x)" "Call f(x) for its effect and return x.";
377    Function: "memoize" "memoize(f)" "f, remembering each argument's answer.";
378    Function: "def_hash" "def_hash(f)" "The engine's identity of the definition f is bound under, as b3: and 64 hex digits: the hash of its resolved tree and @doc, with each definition it names by that one's hash, so it changes exactly when f or anything f reaches changes, never on a rename. It is the def_hash that run's crash traces and receipts name for f. Nil for a function no definition binds (a lambda, a builtin) and in a door that identifies no program (a session, an appliance); blue run, test, profile and trace identify theirs. Reads no host state; raises unless f is a function.";
379    Function: "volatile" "volatile(f)" "Declares that f must run on every call: neither f nor any definition reaching it is answered from the call memo. Nil; raises unless f is a function.";
380    Function: "decorate" "decorate(f, keys-and-values...)" "f with metadata attached; it still calls as f.";
381    Function: "visit" "visit(f, x)" "f(x).";
382    // ── predicates ────────────────────────────────────────────────────────
383    Predicate: "nil?" "nil?(x)" "Whether x is nil (okite D0010). x.nil? says the same and is the spelling blue writes (B0047).";
384    Predicate: "bool?" "bool?(x)" "Whether x is true or false.";
385    Predicate: "boolean?" "boolean?(x)" "The same as bool?.";
386    Predicate: "integer?" "integer?(x)" "Whether x is an Int; integer?(1.0) is false.";
387    Predicate: "float?" "float?(x)" "Whether x is a Float. 6 / 2 is the Int 3, while 7 / 2 is 3.5.";
388    Predicate: "number?" "number?(x)" "Whether x is an Int or a Float.";
389    Predicate: "string?" "string?(x)" "Whether x is a string.";
390    Predicate: "keyword?" "keyword?(x)" "Whether x is a keyword such as :done.";
391    Predicate: "symbol?" "symbol?(x)" "Whether x is a Lisp symbol; false for keywords.";
392    Predicate: "list?" "list?(x)" "Whether x is a list. list?(nil) is true, so test x == nil first.";
393    Predicate: "map?" "map?(x)" "Whether x is a map. A parsed JSON object is not one.";
394    Predicate: "procedure?" "procedure?(x)" "Whether x can be called.";
395    Predicate: "empty?" "empty?(xs)" "Whether xs is nil or has no elements.";
396    Predicate: "null?" "null?(x)" "Whether x is nil or []. The Lisp word; prefer retsu's is_empty.";
397    Predicate: "pair?" "pair?(x)" "Whether x is a non-empty list.";
398    Predicate: "atom?" "atom?(x)" "Whether x is a scalar rather than a list; false for nil.";
399    Predicate: "some?" "some?(x)" "Whether x is not nil.";
400    Predicate: "even?" "even?(n)" "Whether n is even.";
401    Predicate: "odd?" "odd?(n)" "Whether n is odd.";
402    Predicate: "zero?" "zero?(n)" "Whether n is 0.";
403    Predicate: "positive?" "positive?(n)" "Whether n > 0.";
404    Predicate: "negative?" "negative?(n)" "Whether n < 0.";
405    Predicate: "equal?" "equal?(a, b)" "Structural equality; what == lowers to.";
406    Predicate: "eq?" "eq?(a, b)" "Identity: true for the same keyword or string, false for two equal lists. Use ==.";
407    Predicate: "is?" "is?(x, type)" "Whether x has the type named by a keyword: is?(5, :int). Value first.";
408    Predicate: "the" "the(type, x)" "x, after asserting its type: the(:int, 5). Type first.";
409    Predicate: "cast" "cast(type, x)" "x checked against, or narrowed to, the type a keyword names: cast(:float, 5) is 5.0. Type first.";
410    Predicate: "foreign?" "foreign?(x)" "Whether x is an opaque value a host handed in.";
411    Predicate: "promise?" "promise?(x)" "Whether x is a delayed value.";
412    Predicate: "chan?" "chan?(x)" "Whether x is a channel.";
413    Predicate: "go?" "go?(x)" "Whether x is a fiber.";
414    // ── errors ────────────────────────────────────────────────────────────
415    Error: "raise" "raise(kind_or_error[, message[, opts]])" "Raise an error: raise(:not_found, \"no repo #{name}\"); opts is {data: value, cause: e}. raise(e) raises e again unchanged. Uncaught, the run prints kind: message, every cause, the stack, and exits 1. The error's fields are e.kind, e.message, e.data, e.cause and e.trace, the raise site as file:line.";
416    Error: "error" "error(kind, message[, data])" "An error VALUE, raising nothing. throw(error(k, m)) is written raise(k, m) (B0037).";
417    Error: "throw" "throw(err)" "Raise any value. Written raise(err) (B0037).";
418    Error: "error?" "error?(x)" "Whether x is an error value; true inside a catch handler.";
419    Error: "error_message" "error_message(e)" "The message of an error value. Written e.message (B0036).";
420    Error: "error_kind" "error_kind(e)" "The kind of an error value, a keyword. Written e.kind (B0036).";
421    Error: "gensym" "gensym([prefix])" "A fresh unique symbol, for macros that need a private name.";
422    Lisp: "chain_rules" "chain_rules" "Every conversion-chain rewrite the check stage, the formatter and the evaluator share, as [id, slug, outer, soundness, cases] rows; a case is [label, kind or nil, lhs, rhs] with lhs and rhs one-parameter lambda forms for eval. okite's laws evaluate both sides.";
423    Lisp: "builtin_names" "builtin_names" "Every name this reference documents, as strings: what a bare name falls to when no definition or import has it. A catalogue reads it to say which definitions share a builtin's name.";
424    // ── JSON ──────────────────────────────────────────────────────────────
425    Json: "json_parse" "json_parse(text)" "Parse JSON. An object becomes a list of [key, value] pairs, not a map; read it with json_get or deeta. null is nil.";
426    Json: "json_read" "json_read(text)" "Parse JSON with every object a map keyed by its field names, read with get and map_entries; an array is a list, null is nil.";
427    Json: "json_stringify" "json_stringify(v)" "Compact JSON text, a map's keys sorted, a keyword as its name.";
428    Json: "json_canonical" "json_canonical(text)" "The JSON document in text as canonical JSON: every object's keys sorted, no whitespace, numbers and strings as serde_json writes them, so equal documents hash alike. Unlike json_stringify(json_read(text)), an array of [string, value] pairs stays an array. Raises when text is not JSON.";
429    Json: "json_get" "json_get(obj, key)" "The field of a parsed JSON object, or nil. Raises when obj is not an object.";
430    Json: "json_get_or" "json_get_or(obj, key, default)" "The field of a parsed JSON object, or default.";
431    Nix: "nix_parse" "nix_parse(source)" "Nix source as rnix's lossless syntax tree: {root, errors}. A node is {kind, start, end, children}, a token {kind, start, end, text}; kinds are rnix's names (NODE_ATTR_SET, TOKEN_IDENT), offsets count characters, and each error is {message, start, end}. The nikkusu bidama queries it.";
432    Xml: "xml_parse" "xml_parse(text)" "Parse an XML document to its root element, a map {name, attributes, children, text}: name keeps its namespace prefix, attributes maps each name to its unescaped value, children lists the child elements, text joins the element's own character data (whitespace between child elements dropped). Comments, processing instructions and the DOCTYPE are dropped. Only the five predefined entities and character references expand: a DTD entity declaration or any other reference raises :xml_refused, so nothing is fetched or multiplied. A document that is not well-formed raises :xml_malformed. The maakuappu bidama queries it.";
433    Xml: "xml_render" "xml_render(element)" "An element map as XML text, escaped so xml_parse reads it back equal: attributes in key order, an element with no text and no children as <name/>. A name that is not an XML name, a character XML 1.0 cannot hold or a value of the wrong kind raises :xml_refused.";
434    // ── files ─────────────────────────────────────────────────────────────
435    File: "read_file" "read_file(path)" "The file's text. Raises when it cannot be read; shisutemu's read_or(path, default) does not.";
436    File: "write_file" "write_file(path, text)" "Replace the file's contents with text; nil.";
437    File: "write_program" "write_program(path, text)" "Write a blue program in its canonical form: text through the formatter, then to the file; raises when text does not parse. Every producer of a .b file writes with it (B0064).";
438    File: "append_file" "append_file(path, text)" "Add text to the end of the file, creating it; nil.";
439    File: "rename_file" "rename_file(from, to)" "Move a file, atomically on one filesystem: write a temporary beside to, then rename.";
440    File: "rm" "rm(path)" "Delete a file; raises when it is absent.";
441    File: "rm_rf" "rm_rf(path)" "Delete a file or a directory tree; nil when there is nothing.";
442    File: "mkdir" "mkdir(path)" "Create one directory; nil if it exists.";
443    File: "mkdir_p" "mkdir_p(path)" "Create a directory and its parents.";
444    File: "path_exists" "path_exists(path)" "Whether anything is at path.";
445    File: "is_file?" "is_file?(path)" "Whether path is a regular file.";
446    File: "is_dir?" "is_dir?(path)" "Whether path is a directory.";
447    File: "file_size" "file_size(path)" "The size in bytes.";
448    File: "file_mtime_ms" "file_mtime_ms(path)" "The last modification time, in Unix milliseconds.";
449    File: "ls" "ls(dir)" "The entries of a directory as full paths, sorted.";
450    File: "walk_dir" "walk_dir(root)" "Every file under root, as paths, in no fixed order: sort before comparing.";
451    File: "glob" "glob(pattern)" "Paths matching * and ** relative to the current directory. An absolute pattern finds nothing; walk_dir and filter instead.";
452    File: "path_join" "path_join(part, more...)" "The parts joined with /.";
453    File: "path_basename" "path_basename(path)" "The last component: \"b.txt\" of \"a/b.txt\".";
454    File: "path_dirname" "path_dirname(path)" "Everything before the last component.";
455    File: "path_extension" "path_extension(path)" "The extension without its dot, or nil.";
456    File: "cwd" "cwd" "The current directory.";
457    File: "append_log" "append_log(log, record)" "Add one record, a line of text, to the end of an append-only log, creating the log and its directories; nil. A record holding a newline or not a string raises :bad_option, and a log whose last record is torn raises :parse_error, writing nothing. Needs the storage_append capability, which filesystem holds; nothing in it truncates, overwrites or removes.";
458    File: "read_log" "read_log(log)" "Every record append_log wrote to a log, in order, as strings. An absent log raises :not_found, and one whose last record is torn raises :parse_error. Needs the storage_append capability, which filesystem holds.";
459    Device: "play_audio" "play_audio(clip)" "Play a clip, {rate:, channels:, samples:} with the samples interleaved numbers from -1.0 to 1.0, on the default output device, and answer nil once it has played. A clip that is not whole frames of such samples raises :bad_option; a host with no audio system raises :unsupported, and one with no output device :not_found. Needs the audio_out capability.";
460    Device: "audio_out_format" "audio_out_format" "The default output device's format, {rate:, channels:}, or nil when there is none. Needs the audio_out capability.";
461    Device: "capture_audio" "capture_audio(frames, format)" "Capture frames frames (0 to 16777216) from the default input device in format {rate:, channels:}: a clip {rate:, channels:, samples:}. A bad count or format raises :bad_option; a host with no audio system raises :unsupported, one with no input device :not_found, and a device that stalls :timeout. Needs the audio_in capability.";
462    Device: "audio_in_format" "audio_in_format" "The default input device's format, {rate:, channels:}, or nil when there is none. Needs the audio_in capability.";
463    Device: "canvas_open" "canvas_open(opts)" "A black canvas of opts {width:, height:} pixels, 1 to 4096 each, titled opts title: (\"blue\"), shown by the canvas server listening at $BLUE_CANVAS_SOCKET, else $XDG_RUNTIME_DIR/garasu/canvas.sock. No server listening raises :not_found. Needs the display capability.";
464    Device: "canvas_clear" "canvas_clear(canvas, color)" "Fill the canvas with a color, \"#rgb\" or \"#rrggbb\"; nil. What is drawn shows at the next canvas_present. Needs the display capability.";
465    Device: "canvas_fill_rect" "canvas_fill_rect(canvas, x, y, width, height, color)" "Fill a rectangle with its top left at x, y, clipped to the canvas; nil. Needs the display capability.";
466    Device: "canvas_text" "canvas_text(canvas, x, y, text, color)" "Draw text in an 8 by 8 pixel font with its top left at x, y, one cell a character; a character outside printable ASCII draws as ?. nil. Needs the display capability.";
467    Device: "canvas_present" "canvas_present(canvas)" "Show what has been drawn: canvas_pixel reads it, and the canvas server receives it. Needs the display capability.";
468    Device: "canvas_pixel" "canvas_pixel(canvas, x, y)" "The shown color at x, y as \"#rrggbb\", or nil outside the canvas. Needs the display capability.";
469    Device: "canvas_close" "canvas_close(canvas)" "Close the canvas; nil. Closing twice is done, and any other word on a closed canvas raises :closed. Needs the display capability.";
470    // ── processes ─────────────────────────────────────────────────────────
471    Process: "exec_capture" "exec_capture(cmd, args...)" "Run a command, no shell: [[:status, n], [:stdout, s], [:stderr, s]]. Read it with shisutemu's status_of and stdout_of.";
472    Process: "exec_check" "exec_check(cmd, args...)" "Run a command, no shell; its exit status as an Int. Output passes through.";
473    Process: "exec_ok?" "exec_ok?(cmd, args...)" "Whether a command exits 0.";
474    Process: "exec_with_stdin" "exec_with_stdin(input, cmd, args...)" "exec_capture, with input written to the command's stdin. The INPUT comes first.";
475    Process: "exec_exchange" "exec_exchange(bytes, n, ms, cmd, args...)" "One request and reply over a child's stdio, for binary protocols: bytes (Ints 0..=255) are written and stdin stays open until n bytes of stdout arrive, stdout closes or ms pass; stdin then closes, and a child still running at ms is ended. :stdout is the list of bytes read, :status -1 when the child had to be ended.";
476    Process: "exec_with_env" "exec_with_env(env, cmd, args...)" "exec_capture with extra environment variables, given as [[name, value], …].";
477    Process: "exec_into" "exec_into(env, cmd, args...)" "Replace this process with a command run with extra environment variables, given as [[name, value], …]; stdio is inherited. Returns only by raising, when the command cannot start.";
478    Process: "exit_with" "exit_with(code)" "End the program now with this exit status, after flushing stdout and stderr. A typed outcome a caller must branch on (EX_TEMPFAIL 75 for a hold) leaves this way, not as an uncaught error.";
479    Process: "sh_exec" "sh_exec(script)" "Run a script through sh; the same result as exec_capture. Prefer exec_capture, which cannot be injected into.";
480    // ── environment ───────────────────────────────────────────────────────
481    Environment: "getenv" "getenv(name[, default])" "An environment variable, or default (nil when none is given).";
482    Environment: "env_required" "env_required(name)" "An environment variable; raises, naming it, when unset.";
483    Environment: "argv" "argv" "The program's own arguments: what follows the file in blue run file -- a b.";
484    Environment: "argv_get" "argv_get(i[, default])" "One argument by index, or default.";
485    Environment: "self_exe" "self_exe" "The path of the blue running this program, or nil outside the CLI. Spawn it to run blue; a sandbox has no blue on PATH.";
486    // ── time ──────────────────────────────────────────────────────────────
487    Clock: "now" "now" "Unix time in seconds.";
488    Clock: "now_ms" "now_ms" "Unix time in milliseconds.";
489    Clock: "now_ns" "now_ns" "Unix time in nanoseconds.";
490    Clock: "now_rfc3339" "now_rfc3339" "The current UTC time as RFC 3339 text.";
491    Clock: "elapsed_since" "elapsed_since(start_ns)" "Nanoseconds since start_ns, a value of now_ns.";
492    Clock: "sleep" "sleep(seconds)" "Pause for whole seconds.";
493    Clock: "sleep_ms" "sleep_ms(ms)" "Pause for milliseconds.";
494    // ── network ───────────────────────────────────────────────────────────
495    Network: "nats_connect" "nats_connect(url[, timeout_ms])" "A NATS connection to nats://[user:pass@|token@]host[:port] (port 4222), confirmed by a PING before it returns. Plain TCP: a server requiring TLS is refused. Raises when it cannot connect.";
496    Network: "nats_publish" "nats_publish(conn, subject, payload[, reply])" "Publish a string to one subject (no wildcards); nil. With reply, the receiver answers there (a JetStream pull names its inbox this way). Also answers the server's pings, so a publish-only loop stays connected.";
497    Network: "nats_inbox" "nats_inbox(conn)" "A fresh reply subject unique to this connection, for nats_subscribe and the reply of nats_publish.";
498    Network: "nats_request" "nats_request(conn, subject, payload, timeout_ms)" "Publish and wait for one reply; the reply's payload as a string. Raises on timeout and when no one is subscribed (no responders).";
499    Network: "nats_subscribe" "nats_subscribe(conn, subject[, queue])" "A subscription to a subject, wildcards * and > allowed; with a queue group, one member of the group gets each message. Read it with nats_next_message.";
500    Network: "nats_next_message" "nats_next_message(sub, timeout_ms)" "{subject:, payload:, status:, headers:, reply:} for the next message, waiting at most timeout_ms (0 only checks); nil when none came. status is the header block's code (JetStream's 404 and 408) or nil, headers its text or nil. reply is nil unless the sender wants an answer: publish to it.";
501    Network: "nats_unsubscribe" "nats_unsubscribe(sub)" "Stop a subscription; messages already queued for it are dropped. nil.";
502    Network: "nats_close" "nats_close(conn)" "Close the connection; every later call on it raises. nil.";
503    Network: "http_request" "http_request(method, url, headers, body, timeout_ms)" "One HTTP/1.1 request to an http:// url (https is refused): {status:, headers:, body:}, headers as lower-case [name, value] pairs. headers is nil, a map or [[name, value], …]; body is nil or a string. Any status is an answer; only a failed exchange raises.";
504    Network: "tcp_connect_ms" "tcp_connect_ms(host, port, timeout_ms)" "Open one TCP connection to host:port and close it at once: the milliseconds the connect took, the handshake only (name resolution is not counted). A failed connect is an answer, not a raise: [:error, why, detail], why one of :unresolved, :timeout, :refused, :unreachable or :failed. A bad port or timeout raises.";
505    Network: "pg_connect" "pg_connect(url[, timeout_ms])" "A PostgreSQL connection to postgres://user@host[:port]/database (a password, when the server asks for one, rides in the url's userinfo after a colon), speaking the wire protocol over plain TCP: trust, password and SCRAM-SHA-256 authentication; md5 and TLS are refused by name. Raises when it cannot connect.";
506    Network: "pg_query" "pg_query(conn, sql, params)" "One statement with $1… parameters (nil, true/false, numbers or text): {columns:, rows:, command:}, each row a map from column name to value, integers, floats and booleans typed, everything else text.";
507    Network: "pg_execute" "pg_execute(conn, sql)" "A script of statements, unparameterised: the list of their command tags. Raises naming the first failure.";
508    Network: "pg_close" "pg_close(conn)" "Close the connection; every later call on it raises. nil.";
509    Network: "resp_connect" "resp_connect(host, port, password, tls[, timeout_ms])" "A RESP (Valkey, Redis) connection, authenticated with password when it is not nil and confirmed by a PING. tls true is refused by name until the runtime has a TLS transport (docs/HTTP2.md P4); tls false is allowed only for 127.0.0.1. Raises when it cannot connect.";
510    Network: "resp_command" "resp_command(conn, args)" "Send one command, a list of text or numbers, and return its reply: text, an integer, nil or a list. A server error raises.";
511    Network: "resp_pipeline" "resp_pipeline(conn, commands)" "Send every command in one write and return their replies in order. Raises naming the first server error.";
512    Network: "resp_next" "resp_next(conn, timeout_ms)" "The next reply the server pushes (a subscribed connection's messages), waiting at most timeout_ms (0 only checks what has arrived); nil when none came.";
513    Network: "resp_close" "resp_close(conn)" "Close the connection; every later call on it raises. nil.";
514    Network: "http_listen" "http_listen(addr)" "A listening HTTP/1.1 server socket on host:port (port 0 picks a free one). Raises when the address cannot be bound.";
515    Network: "http_listener_port" "http_listener_port(listener)" "The TCP port the listener is bound to.";
516    Network: "http_next_request" "http_next_request(listener, timeout_ms)" "{method:, path:, query:, headers:, body:, exchange:} for the next request, waiting at most timeout_ms (0 only checks); nil when none came. A malformed request is answered 400 and skipped. Answer it with http_respond.";
517    Network: "flash_open" "flash_open(addr_or_wire)" "The Flash channel of a dual-pulse wire: a UDP socket bound to host:port (port 0 picks a free one), or the Flash channel opened on a wire tide_open made, so both share one socket. Needs the comm_flash capability; the host holds each wire to the Flash rate and kodou paces under it.";
518    Network: "flash_put" "flash_put(wire, peer, payload)" "Send one Flash frame (framing byte 0x02, then the payload) to host:port at once, ahead of any Tide frame still queued: the datagram's bytes. A datagram past 1280 bytes raises and sends nothing. The host holds each wire to flash_bytes_per_second (16384) a second and one second's worth at once, on the frame's clock (a whole-program clock provider, else the host's): a send past it raises :rate_limited, catchable because it breaches no capability, with data {bytes:, rate:, burst:, wait_ms:} (wait_ms nil when no wait fits), and sends nothing. kodou::emit paces under it.";
519    Network: "flash_next" "flash_next(wire, timeout_ms)" "{from:, payload:, bytes:} for the next Flash frame, waiting at most timeout_ms (0 only checks); nil when none came. A datagram that is no frame or no UTF-8 is dropped and counted as malformed.";
520    Network: "flash_stats" "flash_stats(wire)" "{address:, port:, sent:, received:, dropped:, malformed:, refused:, rate:} of the wire's Flash channel; dropped counts frames past its 256-frame inbox, refused the sends its rate refused, rate its bytes a second.";
521    Network: "flash_close" "flash_close(wire)" "Close the wire's Flash channel; the socket closes with its last channel. nil.";
522    Network: "tide_open" "tide_open(addr_or_wire[, opts])" "The Tide channel of a dual-pulse wire, bound to host:port or joined to a wire flash_open made; opts {ring_frames:, ring_bytes:} hold its send ring and its inbox each to that many frames (64) and bytes (65536, at least 1280). Needs the comm_scavenger capability.";
523    Network: "tide_put" "tide_put(wire, peer, payload)" "Queue one Tide frame for host:port in the send ring: how many of the oldest queued frames it evicted to fit. A frame past 1280 bytes with its 9-byte header raises.";
524    Network: "tide_release" "tide_release(wire, max_bytes, stamp_ms)" "Send queued Tide frames from the oldest while their datagrams total at most max_bytes, each stamped stamp_ms (framing byte 0x01, the stamp as 8 bytes, then the payload): the bytes sent. kodou passes the window its LEDBAT controller allows.";
525    Network: "tide_send" "tide_send(wire, peer, payload, stamp_ms)" "Send one Tide frame at once, past the ring: the datagram's bytes. For an acknowledgement, which no window holds.";
526    Network: "tide_next" "tide_next(wire, timeout_ms)" "{from:, payload:, bytes:, sent_ms:} for the oldest Tide frame in the inbox, waiting at most timeout_ms (0 only checks); nil when none came. The inbox evicts its oldest frame past its bounds.";
527    Network: "tide_stats" "tide_stats(wire)" "{address:, port:, sent:, received:, dropped:, malformed:, queued:, queued_bytes:, evicted:, evicted_in:, ring_frames:, ring_bytes:} of the wire's Tide channel.";
528    Network: "tide_close" "tide_close(wire)" "Close the wire's Tide channel and drop what its ring holds; the socket closes with its last channel. nil.";
529    Network: "http_respond" "http_respond(request, status, headers, body)" "Answer a request from http_next_request once and close its connection; headers as for http_request, body nil or a string. A second answer raises. nil.";
530    // ── crypto ────────────────────────────────────────────────────────────
531    Crypto: "blake3_hex" "blake3_hex(data)" "The BLAKE3-256 hash of a string or byte list, in lowercase hex. shomei's hash_message wraps it.";
532    Crypto: "digest_hex" "digest_hex(algorithm, data)" "The digest of a string or byte list in lowercase hex. The algorithm is \"sha1\", \"sha256\", \"sha512\" or \"blake3\" (a string or a keyword); any other name raises. shomei's hash wraps it.";
533    Crypto: "hmac_hex" "hmac_hex(algorithm, key, data)" "The HMAC of data under key in lowercase hex, for \"sha1\", \"sha256\" or \"sha512\"; key and data are a string or byte list, and an empty key is allowed. blake3 and unknown names raise. shomei's hash with key: wraps it.";
534    Crypto: "bytes_equal?" "bytes_equal?(a, b)" "Whether two strings or byte lists hold the same bytes. It reads every position of the longer side whether or not an earlier byte differs. A difference in length is false. shomei's same? wraps it.";
535    Crypto: "random_bytes" "random_bytes(n)" "A list of n random byte integers (0..=255) from the operating system, n from 0 to 1048576. Raises when n is not an integer in range or the source cannot be read. Needs the entropy capability.";
536    Crypto: "ed25519_keypair" "ed25519_keypair(seed_hex)" "[secret, public] in hex for a 32-byte seed. The caller supplies the seed; random_bytes draws one.";
537    Crypto: "ed25519_sign" "ed25519_sign(secret_hex, message)" "The Ed25519 signature of a message, in hex.";
538    Crypto: "ed25519_verify" "ed25519_verify(public_hex, message, signature_hex)" "Whether the signature is valid.";
539    Crypto: "threshold_deal" "threshold_deal(k, n)" "A k-of-n threshold group (RFC 9591 FROST over Ed25519, 2 <= k <= n) from one trusted dealer: {group:, key:, min:, max:, shares:}. group is the public key package in hex, key the 32-byte Ed25519 verifying key in hex, each share {participant:, key:, min:, secret:} whose secret prints as foreign. The dealer holds the whole key while it runs; threshold_dkg_start makes a group no process holds whole. Needs the mpc_threshold capability.";
540    Crypto: "threshold_dkg_start" "threshold_dkg_start(participant, k, n)" "Round one of distributed key generation for one participant: {secret:, broadcast: {from:, package:}}. Send the broadcast to every participant and keep the secret, which only threshold_dkg_exchange reads, once.";
541    Crypto: "threshold_dkg_exchange" "threshold_dkg_exchange(secret, broadcasts)" "Round two, with the secret threshold_dkg_start answered: checks every other participant's broadcast and its proof of knowledge and answers {secret:, sends: [{from:, to:, package:}]}; send each package only to the participant named in to. Its own broadcast in the list is ignored; a duplicate or unknown sender, a missing one or a failed proof raises :refused.";
542    Crypto: "threshold_dkg_finish" "threshold_dkg_finish(secret, broadcasts, sends)" "Round three, with the secret threshold_dkg_exchange answered: checks the packages sent to this participant against their broadcasts and answers {share:, group:, key:}. Packages for other participants are ignored. No participant ever holds the group's secret.";
543    Crypto: "threshold_commit" "threshold_commit(share, message)" "Signing round one for one share: {nonces:, commitment: {participant:, hex:}}. Send the commitment to the coordinator and keep the nonces, which sign once.";
544    Crypto: "threshold_sign" "threshold_sign(share, nonces, message, commitments)" "Signing round two: this share's signature share {participant:, hex:} over the message for these commitments, at least k of them and its own among them. The nonces are spent; a second use, nonces made for another message or share, or too few commitments raises :refused.";
545    Crypto: "threshold_aggregate" "threshold_aggregate(group, message, commitments, signature_shares)" "The 64-byte Ed25519 signature in hex from at least k signature shares, checked under the group's key, so ed25519_verify(key, message, sig) is true. Fewer than k raises :refused with {have:, need:}; a share that does not verify raises :tampered with {culprits:}.";
546    Crypto: "threshold_share_export" "threshold_share_export(share)" "The whole secret share in hex, for its holder's own storage; the one word that prints it. threshold_share_import reads it back.";
547    Crypto: "threshold_share_import" "threshold_share_import(hex)" "A share from threshold_share_export's hex, {participant:, key:, min:, secret:}. Raises :decode_error when the hex is no share.";
548    // ── output ────────────────────────────────────────────────────────────
549    Output: "write_stdout" "write_stdout(text)" "Write text to stdout exactly, no newline added. The way a command prints.";
550    Output: "write_stderr" "write_stderr(text)" "Write text to stderr exactly.";
551    Output: "println" "println(xs...)" "Print values with a newline, strings with their quotes. For output a person reads, use write_stdout.";
552    Output: "print" "print(x)" "Print a value and a newline, a string with its quotes.";
553    Output: "display" "display(x)" "Print a value's Lisp rendering, no newline.";
554    Output: "newline" "newline" "Print a newline.";
555    // ── delayed values ────────────────────────────────────────────────────
556    Lazy: "force" "force(p)" "The value of a promise, computed once; a non-promise is returned as is.";
557    Lazy: "cycle" "cycle(xs)" "A tatara-lisp lazy sequence repeating xs. blue's list words do not force it: take(2, cycle(xs)) is not a plain list.";
558    Lazy: "realize" "realize(lazy)" "A finite tatara-lisp lazy sequence forced into a list. On anything built from cycle it does not terminate.";
559    // ── concurrency ───────────────────────────────────────────────────────
560    Concurrency: "chan" "chan([capacity])" "A tatara-lisp channel. Its put and take words (>! and <!) have no blue spelling.";
561    Concurrency: "close!" "close!(ch)" "Close a channel.";
562    Concurrency: "drain!" "drain!(ch)" "Every value waiting in a channel, as a list.";
563    Concurrency: "go" "go(f)" "A pending fiber for a zero-argument function. Running it (go-run) has no blue spelling.";
564    // ── blue processes ────────────────────────────────────────────────────
565    Actor: "spawn" "spawn(f[, args][, opts])" "Start a process running f(args...) and return its pid. f's code and copies of the data it reaches go with it; a host handle does not, so open it inside. Processes run while the program waits (receive); the program ends when it does. opts {scheduling: :normal | :idle}: :normal (the default) changes nothing; :idle runs each of its slices on a thread the OS gives its lowest priority (nice 19 on Linux, the background band on darwin), apart from the node: the node runs its other processes while that thread waits for a core, and the process still acts only while the program waits. A host with no such class raises :unsupported. :realtime, the class the OS never preempts, raises :permission_denied: the OS gives it only to a privileged process.";
566    Actor: "spawn_link" "spawn_link(f[, args][, opts])" "spawn, linked to the caller: an abnormal exit of either ends the other, unless it traps exits. opts as for spawn.";
567    Actor: "spawn_server" "spawn_server(handler, state[, opts])" "A process that answers each message m with state = handler(m, state). Its state is what defmigrate transforms and evict carries. opts as for spawn, and dispatch_steps: n, the most steps one message's handler may take, met with the project's bounds({dispatch_steps:}) so each only narrows. Past it the process exits [:runaway, \"execution budget exhausted: steps per dispatch (limit n)\"], as a step bound ends one; nothing the handler did to the state lands, and a supervisor restarts it from the state before that message. A positive integer, or :bad_option.";
568    Actor: "send" "send(pid, msg)" "Copy msg into pid's mailbox and return msg. A function or handle is refused; a send to a process that has exited is dropped.";
569    Actor: "receive" "receive([pattern][, timeout_ms][, on_timeout])" "The oldest message matching pattern (:_ matches anything, a list or map matches by shape), waiting for one. After timeout_ms, on_timeout (default nil). Waiting forever with no possible sender raises a deadlock.";
570    Actor: "self" "self" "This process's pid. The program itself is 0.";
571    Actor: "alive?" "alive?(pid)" "true while pid is running or waiting.";
572    Actor: "link" "link(pid)" "Link to pid: an abnormal exit of either ends the other. false when pid is not alive.";
573    Actor: "unlink" "unlink(pid)" "Remove a link; true.";
574    Actor: "monitor" "monitor(pid)" "Watch pid and return a ref; its exit arrives as [:down, ref, pid, reason] ([:down, ref, pid, :noproc] if it is gone).";
575    Actor: "demonitor" "demonitor(ref)" "Stop a monitor; true.";
576    Actor: "exit" "exit(pid, reason)" "Send pid an exit signal: it ends with reason unless it traps exits (:normal ends nothing, :kill cannot be trapped). exit(self, r) raises.";
577    Actor: "trap_exit" "trap_exit(flag)" "While true, exit signals arrive as [:exit, pid, reason] messages instead of ending this process. Returns the previous flag.";
578    Actor: "supervisor" "supervisor(strategy, children[, max_restarts])" "Start children (functions, [f, args] pairs, or [:server, handler, state[, opts]] with spawn_server's opts) under :one_for_one, :one_for_all or :rest_for_one and return the supervisor's pid. A crashed child restarts with an empty mailbox; past max_restarts (default 3) the supervisor gives up. Each crash writes a trace for blue replay.";
579    Actor: "children" "children(sup)" "The pids a supervisor started, in order.";
580    Actor: "defmigrate" "defmigrate(target, new_fn[, transform])" "Swap the code of process target, or of every process running that function, at its next boundary without dropping its mailbox; a server's state becomes transform(state). A migration that raises leaves the old code running. Returns [[pid, :ok or [:error, why]], …].";
581    Actor: "evict" "evict(pid, wire, peer)" "Move process pid off this node at its next message boundary: a server between two messages, with its code, state and its own dispatch_steps; a plain process (spawn) parked at receive, with its code and its VM frames, so its locals, the closures it holds and the cells they share go on where they were, what is left of a receive timeout, and its tape, so a crash on the peer replays from its start (a tape the send ring cannot hold stays behind, and the process moves without it). Its scheduling class and waiting messages go too; a plain process still running stops at its next receive and takes nothing more here. They become a blue-image, queued as Tide frames for peer (host:port) on wire, and the process leaves the node: its monitors get [:down, ref, pid, :evicted], and each linked process, which must trap exits, gets [:exit, pid, :evicted] and is linked no more. A supervised process's supervisor drops it, and the image carries its restart: on the peer it runs under a one_for_one supervisor of its own that restarts it as its old one would (a server from the state its old one began it with, a plain process from its code), its restarts so far counted against the same max_restarts. tide_release sends the frames at the pace the program allows; accept on the peer's wire resumes it. Returns {image: \"b3:…\", frames:, bytes:}. Only the program evicts, since it waits for the boundary. Refused by name, and the process keeps running here: the program itself (the door runs it on the tree-walker, not on a VM this node holds), a supervisor, a plain process whose frames hold a value with no data form (a host handle, a promise, a tree-walker closure) or run code a migration replaced, a linked process whose partner does not trap exits, and an image the send ring has no room for. Needs the comm_scavenger capability.";
582    Actor: "accept" "accept(wire, timeout_ms)" "Resume a process another node evicted to this wire: waits at most timeout_ms (0 only checks) for every frame of one blue-image, checks it hashes to its name, starts it here with the same code, state or frames, scheduling class and waiting messages, a server's dispatch_steps met with this node's bound, and returns its new pid; nil when no whole image came. A plain process goes on inside the receive it moved at, its functions rebuilt from its code, and its tape starts with the entries it made before the move, so a crash trace of it here replays from its start (blue replay refuses one whose carried entries are not the ones that came); an image with no tape, from an older blue or a tape past its bound, starts a tape marked truncated. A supervised process arrives under a one_for_one supervisor of its own, which restarts it as its old one would. A frame of the image that is malformed, or an image whose bytes hash to another name, raises :decode_error. Frames this node cannot resume (a native function it does not register, code its blue compiles to another chunk) are refused by name and nothing is admitted; the process has already left the node it came from. Other Tide frames stay in the inbox for tide_next. An accepted process runs under this node's frame, never more. Needs the comm_scavenger capability.";
583};
584
585/// The row for a name, if blue describes it.
586#[must_use]
587pub fn doc_of(name: &str) -> Option<&'static NameDoc> {
588    NAMES.iter().find(|d| d.name == name)
589}
590
591#[must_use]
592pub fn signatures() -> &'static blue_lang_check::contracts::Signatures {
593    static SIGNATURES: std::sync::OnceLock<blue_lang_check::contracts::Signatures> =
594        std::sync::OnceLock::new();
595    SIGNATURES.get_or_init(|| {
596        let rows: Vec<blue_lang_check::contracts::BuiltinRow> = NAMES
597            .iter()
598            .map(|d| blue_lang_check::contracts::BuiltinRow {
599                name: d.name,
600                signature: d.signature,
601                kinded: d.topic.kinded(),
602                guard: d.topic == Topic::Predicate && !matches!(d.name, "nil?" | "some?"),
603            })
604            .collect();
605        blue_lang_check::contracts::Signatures::new(&rows)
606    })
607}
608
609impl Topic {
610    #[must_use]
611    pub const fn kinded(self) -> bool {
612        matches!(
613            self,
614            Topic::Number | Topic::Text | Topic::List | Topic::Map | Topic::Function
615        )
616    }
617}