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    Crypto,
57    /// Delayed values.
58    Lazy,
59    /// tatara-lisp's channels and fibers.
60    Concurrency,
61    /// blue's own processes: spawn, mailboxes, links, supervision.
62    Actor,
63    /// Printing to the terminal.
64    Output,
65    /// The master verbs, dispatched on the subject's space.
66    Dispatch,
67}
68
69impl Topic {
70    /// Every topic, in the order a reference presents them.
71    pub const ALL: &'static [Topic] = &[
72        Topic::Form,
73        Topic::Number,
74        Topic::Text,
75        Topic::List,
76        Topic::Map,
77        Topic::Dispatch,
78        Topic::Function,
79        Topic::Predicate,
80        Topic::Error,
81        Topic::Json,
82        Topic::Nix,
83        Topic::Xml,
84        Topic::File,
85        Topic::Process,
86        Topic::Environment,
87        Topic::Clock,
88        Topic::Network,
89        Topic::Crypto,
90        Topic::Output,
91        Topic::Lazy,
92        Topic::Concurrency,
93        Topic::Actor,
94        Topic::Lisp,
95    ];
96
97    /// The heading a reference gives the topic.
98    #[must_use]
99    pub fn title(self) -> &'static str {
100        match self {
101            Topic::Form => "Forms",
102            Topic::Lisp => "tatara-lisp forms",
103            Topic::Number => "Numbers",
104            Topic::Text => "Strings",
105            Topic::List => "Lists",
106            Topic::Map => "Maps",
107            Topic::Function => "Functions",
108            Topic::Predicate => "Predicates",
109            Topic::Error => "Errors",
110            Topic::Json => "JSON",
111            Topic::Nix => "Nix source",
112            Topic::Xml => "XML",
113            Topic::File => "Files and paths",
114            Topic::Process => "Processes",
115            Topic::Environment => "Environment and arguments",
116            Topic::Clock => "Time",
117            Topic::Network => "Network",
118            Topic::Crypto => "Hashing and signatures",
119            Topic::Lazy => "Delayed values",
120            Topic::Concurrency => "Channels and fibers",
121            Topic::Actor => "Blue processes and supervision",
122            Topic::Output => "Terminal output",
123            Topic::Dispatch => "Master verbs",
124        }
125    }
126
127    /// What calling a name of this topic may do besides compute its answer
128    /// from its arguments; `None` when that is all it does. Exhaustive, so a
129    /// new topic decides here. The call memo (`blue-lang-mondou`'s `reach`)
130    /// answers a call from its cache only when nothing the definition reaches
131    /// has an effect.
132    #[must_use]
133    pub fn effect(self) -> Option<Effect> {
134        match self {
135            Topic::Form
136            | Topic::Lisp
137            | Topic::Number
138            | Topic::Text
139            | Topic::List
140            | Topic::Map
141            | Topic::Function
142            | Topic::Predicate
143            | Topic::Error
144            | Topic::Json
145            | Topic::Nix
146            | Topic::Xml
147            | Topic::Crypto
148            | Topic::Lazy => None,
149            Topic::File | Topic::Process | Topic::Environment | Topic::Clock | Topic::Network => {
150                Some(Effect::Host)
151            }
152            Topic::Output => Some(Effect::Output),
153            Topic::Concurrency => Some(Effect::Concurrency),
154            Topic::Actor => Some(Effect::Actor),
155            Topic::Dispatch => Some(Effect::Dispatch),
156        }
157    }
158}
159
160/// What a call can do besides answer ([`Topic::effect`]).
161#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
162pub enum Effect {
163    /// Reaches the host: files, processes, the environment, the clock, the
164    /// network (`blue_lang_waku::Capability::host_effects`).
165    Host,
166    /// Writes to the terminal.
167    Output,
168    /// Talks to a channel or a fiber.
169    Concurrency,
170    /// Talks to a blue process.
171    Actor,
172    /// Runs the handler a space registered for a master verb, which the
173    /// program can replace between two equal calls.
174    Dispatch,
175}
176
177impl Effect {
178    #[must_use]
179    pub fn label(self) -> &'static str {
180        match self {
181            Effect::Host => "host",
182            Effect::Output => "output",
183            Effect::Concurrency => "concurrency",
184            Effect::Actor => "process",
185            Effect::Dispatch => "dispatch",
186        }
187    }
188}
189
190/// One bound name, described.
191#[derive(Clone, Copy, Debug)]
192pub struct NameDoc {
193    pub name: &'static str,
194    pub topic: Topic,
195    /// How a blue program calls it. See the module docs for the grammar.
196    pub signature: &'static str,
197    /// One line: what it returns, and the trap in it when there is one.
198    pub doc: &'static str,
199}
200
201/// `names! { topic: "name" "signature" "doc"; … }` — one row per name, so the
202/// table reads as a table.
203macro_rules! names {
204    ($($topic:ident: $name:literal $sig:literal $doc:literal;)*) => {
205        &[$(NameDoc { name: $name, topic: Topic::$topic, signature: $sig, doc: $doc },)*]
206    };
207}
208
209/// Every name the full interpreter binds that a blue identifier can spell.
210pub const NAMES: &[NameDoc] = names! {
211    // ── forms ─────────────────────────────────────────────────────────────
212    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.";
213    Form: "define" "define(name, value)" "Bind a name. Written name = value; the formatter prints that form.";
214    Form: "set!" "set!(name, value)" "Rebind an existing name. A name a forked process inherited is sealed and refuses it.";
215    Form: "lambda" "fn(x) … end" "The function form fn lowers to. Write fn.";
216    Form: "quasiquote" "quote … end" "What quote with unquote lowers to inside defmacro.";
217    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).";
218    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.";
219    Form: "comment" "comment(anything...)" "Ignores its arguments and is nil. Use # comments instead.";
220    Form: "eval" "eval(form)" "Evaluate a quoted form.";
221    Form: "macroexpand" "macroexpand(form)" "The expansion of a quoted macro call, for debugging a defmacro.";
222    Form: "delay" "delay(expr)" "A promise that evaluates expr once, on the first force.";
223    // ── tatara-lisp forms ─────────────────────────────────────────────────
224    Lisp: "let" "(let ((x 1)) …)" "Lisp binding form; its binding list has no blue spelling. In blue, write x = 1.";
225    Lisp: "letrec" "(letrec ((f …)) …)" "Lisp recursive binding; its binding list has no blue spelling. In blue, use def.";
226    Lisp: "cond" "(cond (test expr) …)" "Lisp multi-way conditional; its clauses have no blue spelling. In blue, use if … elsif … end.";
227    Lisp: "require" "(require …)" "tatara-lisp's module loader. In blue, use(\"pkg\") imports a bidama.";
228    Lisp: "provide" "(provide …)" "tatara-lisp's module export. A bidama exports every def.";
229    Lisp: "dolist" "(dolist (x xs) …)" "Lisp loop; its binding list has no blue spelling. In blue, map(fn(x) … end, xs).";
230    Lisp: "doseq" "(doseq (x xs) …)" "Same as dolist. In blue, map(fn(x) … end, xs).";
231    Lisp: "dotimes" "(dotimes (i n) …)" "Lisp counted loop. In blue, map(fn(i) … end, range(n)).";
232    Lisp: "match" "(match v (pattern expr) …)" "Lisp pattern matching; its patterns have no blue spelling. blue's case compares values and does not destructure.";
233    Lisp: "defflow" "defflow(name, f, g, more...)" "Define name as a function that pipes its argument through f, then g: defflow(slug, trim, downcase).";
234    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.";
235    Lisp: "defstrategy" "defstrategy(name, :variant, f, …, :default, g)" "Define name(variant, args...) dispatching to the function under that keyword, or :default.";
236    Lisp: "defvisitor" "defvisitor(name, :tag, f, …, :default, g)" "Define name(tagged) dispatching on the first element of a tagged list.";
237    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.";
238    Lisp: "defcommand" "defcommand(bus, name, params, body)" "Register a command on a tatara-lisp command bus; params is a Lisp list.";
239    Lisp: "defquery" "defquery(bus, name, params, body)" "Register a query on a tatara-lisp command bus; params is a Lisp list.";
240    // ── numbers ───────────────────────────────────────────────────────────
241    Number: "abs" "abs(x)" "Absolute value, keeping Int or Float.";
242    Number: "min" "min(x, more...)" "The smallest argument. Takes numbers, not a list: apply(min, xs).";
243    Number: "max" "max(x, more...)" "The largest argument. Takes numbers, not a list: apply(max, xs).";
244    Number: "inc" "inc(x)" "x + 1.";
245    Number: "dec" "dec(x)" "x - 1.";
246    Number: "floor" "floor(x)" "The largest Int not above x. floor(a / b) is integer division.";
247    Number: "ceiling" "ceiling(x)" "The smallest Int not below x.";
248    Number: "round" "round(x)" "The nearest Int, halves away from zero: round(2.5) is 3, round(-2.5) is -3.";
249    Number: "truncate" "truncate(x)" "The Int part of x, toward zero: truncate(-2.7) is -2.";
250    Number: "mod" "mod(a, b)" "Euclidean remainder, never negative; what % lowers to.";
251    Number: "modulo" "modulo(a, b)" "The same as mod.";
252    Number: "rem" "rem(a, b)" "The same as mod here: mod(-7, 3), modulo and rem are all 2.";
253    Number: "gcd" "gcd(xs...)" "Greatest common divisor of the arguments; 0 for none.";
254    Number: "lcm" "lcm(xs...)" "Least common multiple of the arguments.";
255    Number: "expt" "expt(base, power)" "base raised to power: expt(2, 10) is 1024.";
256    Number: "sqrt" "sqrt(x)" "Square root, always a Float: sqrt(16) is 4.0, and 4.0 == 4.";
257    Number: "exp" "exp(x)" "e to the x, a Float.";
258    Number: "log" "log(x[, base])" "Natural logarithm, or log to base: log(100, 10) is 2.0.";
259    Number: "sin" "sin(x)" "Sine of x radians.";
260    Number: "cos" "cos(x)" "Cosine of x radians.";
261    Number: "tan" "tan(x)" "Tangent of x radians.";
262    Number: "asin" "asin(x)" "Arcsine; refuses x outside [-1, 1] rather than returning NaN.";
263    Number: "acos" "acos(x)" "Arccosine; refuses x outside [-1, 1] rather than returning NaN.";
264    Number: "atan" "atan(x)" "Arctangent in radians.";
265    Number: "atan2" "atan2(y, x)" "The angle of the point (x, y); y comes first.";
266    Number: "hypot" "hypot(x, y)" "sqrt(x * x + y * y) without the overflow: hypot(3, 4) is 5.0.";
267    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).";
268    Number: "to_int!" "to_int!(x)" "Like to_int, but raises instead of returning nil.";
269    Number: "to_float" "to_float(x)" "A Float from a number or a numeric string.";
270    Number: "compare" "compare(a, b)" "-1, 0 or 1. Total over two numbers or two strings, which < is not; mixing them raises.";
271    // ── strings ───────────────────────────────────────────────────────────
272    Text: "concat" "concat(x, more...)" "The text of every argument joined, each rendered with to_s. What interpolation lowers to.";
273    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.";
274    Text: "string" "string(x)" "tatara-lisp's rendering: :a stays \":a\" and a list prints as (1). Use to_s.";
275    Text: "upcase" "upcase(s)" "The string in upper case. Raises on nil.";
276    Text: "downcase" "downcase(s)" "The string in lower case. Raises on nil.";
277    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.";
278    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.";
279    Text: "join" "join(xs, sep)" "The elements' text with sep between. The LIST comes first here, and the string first in split.";
280    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).";
281    Text: "unlines" "unlines(xs)" "The elements' text, each followed by a newline: text that ends in a line break (B0065).";
282    Text: "line_break" "line_break" "The one-character line-break string, \"\\n\" (B0065). `newline` prints one.";
283    Text: "replace" "replace(s, from, to)" "Every occurrence of from replaced by to.";
284    Text: "chars" "chars(s)" "The characters as one-character strings.";
285    Text: "starts_with?" "starts_with?(s, prefix)" "Whether s begins with prefix.";
286    Text: "ends_with?" "ends_with?(s, suffix)" "Whether s ends with suffix.";
287    Text: "contains?" "contains?(s, sub)" "Whether the STRING s contains sub. On a list it raises: use member?(x, xs).";
288    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.";
289    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.";
290    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].";
291    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.";
292    // ── lists ─────────────────────────────────────────────────────────────
293    List: "list" "list(xs...)" "A list of the arguments. Written [a, b].";
294    List: "length" "length(seq)" "The length of a list or string. Raises on nil: retsu's size(xs) is total.";
295    List: "count" "count(x)" "The number of elements; 0 for nil.";
296    List: "nth" "nth(i, xs)" "The element at index i, from 0; nil past either end. The INDEX comes first.";
297    List: "first" "first(xs)" "The first element; raises on []. retsu's first answers nil.";
298    List: "second" "second(xs)" "The second element; raises when there is none.";
299    List: "third" "third(xs)" "The third element; raises when there is none.";
300    List: "fourth" "fourth(xs)" "The fourth element; raises when there is none.";
301    List: "last" "last(xs)" "The last element; raises on []. retsu's last answers nil.";
302    List: "rest" "rest(xs)" "All but the first; raises on []. retsu's rest answers [].";
303    List: "next" "next(xs)" "All but the first; raises on [].";
304    List: "butlast" "butlast(xs)" "All but the last; raises on [].";
305    List: "car" "car(xs)" "The first element. Prefer first.";
306    List: "cdr" "cdr(xs)" "All but the first ([] for a one-element list); raises on []. Prefer rest.";
307    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.";
308    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.";
309    List: "take" "take(n, xs)" "The first n elements, or all of them. The COUNT comes first.";
310    List: "drop" "drop(n, xs)" "All but the first n. The COUNT comes first.";
311    List: "reverse" "reverse(seq)" "The list or string backwards. Raises on nil.";
312    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.";
313    List: "flatten" "flatten(xs)" "Every nested list spliced in, to any depth.";
314    List: "distinct" "distinct(xs)" "The elements with repeats removed, first occurrence kept, compared with ==.";
315    List: "frequencies" "frequencies(xs)" "A map from each element to how many times it occurs.";
316    List: "zip" "zip(xs, ys)" "[[x1, y1], [x2, y2], …], as long as the shorter list.";
317    List: "interleave" "interleave(xs, ys)" "[x1, y1, x2, y2, …], as long as the shorter list allows.";
318    List: "intersperse" "intersperse(sep, xs)" "xs with sep between each pair. The SEPARATOR comes first.";
319    List: "position" "position(x, xs)" "The index of the first element == x, or -1.";
320    List: "member?" "member?(x, xs)" "Whether some element == x. The ELEMENT comes first.";
321    List: "partition" "partition(f, xs)" "[kept, rejected]: the elements f accepts, then the rest.";
322    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.";
323    List: "iterate" "iterate(f, x, n)" "[x, f(x), f(f(x)), …], n elements.";
324    List: "repeatedly" "repeatedly(f, n)" "A list of n calls to the zero-argument function f. The FUNCTION comes first.";
325    // ── maps ──────────────────────────────────────────────────────────────
326    Map: "get" "get(m, k)" "The value at key k, or nil. Raises on nil and on a list.";
327    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.";
328    Map: "dissoc" "dissoc(m, k)" "A new map without k; unchanged when k is absent.";
329    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.";
330    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.";
331    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.";
332    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.";
333    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.";
334    Dispatch: "need" "need(store, key, more...)" "One entry, raising :not_found when absent. A plain map goes to jisho.";
335    Dispatch: "has?" "has?(store, key, more...)" "Whether the key is present. A plain map goes to jisho.";
336    Dispatch: "put" "put(store, key, value, more...)" "Create or replace an entry; a plain map goes to jisho, which returns a new map.";
337    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.";
338    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.";
339    Dispatch: "create" "create(store, key, value, more...)" "Add a new entry, refusing to overwrite; in_space(\"gitto\", repo) makes a branch or tag.";
340    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.";
341    Dispatch: "info" "info(subject, more...)" "The current state of the subject as a record: in_space(\"gitto\", repo) is the repository's state.";
342    Dispatch: "exists?" "exists?(subject, more...)" "Whether the subject is there; in_space(\"gitto\", repo) asks about a ref.";
343    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.";
344    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.";
345    Map: "zipmap" "zipmap(keys, values)" "A map pairing keys with values, as long as the shorter list.";
346    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.";
347    // ── functions ─────────────────────────────────────────────────────────
348    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 [].";
349    Function: "filter" "filter(f, xs)" "The elements f accepts. The FUNCTION comes first.";
350    Function: "remove" "remove(f, xs)" "The elements f rejects.";
351    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.";
352    Function: "foldl" "foldl(f, init, xs)" "reduce with an init: f(acc, x) from the left.";
353    Function: "foldr" "foldr(f, init, xs)" "Fold from the right: f(x, acc).";
354    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.";
355    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.";
356    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.";
357    Function: "some" "some(f, xs)" "true when f accepts an element, else nil (not false).";
358    Function: "any?" "any?(f, xs)" "Whether f accepts some element; false for [].";
359    Function: "every?" "every?(f, xs)" "Whether f accepts every element; true for [].";
360    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.";
361    Function: "identity" "identity(x)" "x.";
362    Function: "const" "const(x)" "A function that ignores its argument and returns x.";
363    Function: "comp" "comp(f, g)" "The function x -> f(g(x)).";
364    Function: "compose" "compose(fs...)" "Right to left: compose(f, g)(x) is f(g(x)).";
365    Function: "pipe" "pipe(fs...)" "Left to right: pipe(f, g)(x) is g(f(x)).";
366    Function: "partial" "partial(f, args...)" "f with its first arguments fixed: partial(f, 1)(2) is f(1, 2).";
367    Function: "flip" "flip(f)" "f with its two arguments swapped.";
368    Function: "juxt" "juxt(fs...)" "The function x -> [f(x), g(x), …].";
369    Function: "tap" "tap(f, x)" "Call f(x) for its effect and return x.";
370    Function: "memoize" "memoize(f)" "f, remembering each argument's answer.";
371    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.";
372    Function: "decorate" "decorate(f, keys-and-values...)" "f with metadata attached; it still calls as f.";
373    Function: "visit" "visit(f, x)" "f(x).";
374    // ── predicates ────────────────────────────────────────────────────────
375    Predicate: "nil?" "nil?(x)" "Whether x is nil (okite D0010). x.nil? says the same and is the spelling blue writes (B0047).";
376    Predicate: "bool?" "bool?(x)" "Whether x is true or false.";
377    Predicate: "boolean?" "boolean?(x)" "The same as bool?.";
378    Predicate: "integer?" "integer?(x)" "Whether x is an Int; integer?(1.0) is false.";
379    Predicate: "float?" "float?(x)" "Whether x is a Float. 6 / 2 is the Int 3, while 7 / 2 is 3.5.";
380    Predicate: "number?" "number?(x)" "Whether x is an Int or a Float.";
381    Predicate: "string?" "string?(x)" "Whether x is a string.";
382    Predicate: "keyword?" "keyword?(x)" "Whether x is a keyword such as :done.";
383    Predicate: "symbol?" "symbol?(x)" "Whether x is a Lisp symbol; false for keywords.";
384    Predicate: "list?" "list?(x)" "Whether x is a list. list?(nil) is true, so test x == nil first.";
385    Predicate: "map?" "map?(x)" "Whether x is a map. A parsed JSON object is not one.";
386    Predicate: "procedure?" "procedure?(x)" "Whether x can be called.";
387    Predicate: "empty?" "empty?(xs)" "Whether xs is nil or has no elements.";
388    Predicate: "null?" "null?(x)" "Whether x is nil or []. The Lisp word; prefer retsu's is_empty.";
389    Predicate: "pair?" "pair?(x)" "Whether x is a non-empty list.";
390    Predicate: "atom?" "atom?(x)" "Whether x is a scalar rather than a list; false for nil.";
391    Predicate: "some?" "some?(x)" "Whether x is not nil.";
392    Predicate: "even?" "even?(n)" "Whether n is even.";
393    Predicate: "odd?" "odd?(n)" "Whether n is odd.";
394    Predicate: "zero?" "zero?(n)" "Whether n is 0.";
395    Predicate: "positive?" "positive?(n)" "Whether n > 0.";
396    Predicate: "negative?" "negative?(n)" "Whether n < 0.";
397    Predicate: "equal?" "equal?(a, b)" "Structural equality; what == lowers to.";
398    Predicate: "eq?" "eq?(a, b)" "Identity: true for the same keyword or string, false for two equal lists. Use ==.";
399    Predicate: "is?" "is?(x, type)" "Whether x has the type named by a keyword: is?(5, :int). Value first.";
400    Predicate: "the" "the(type, x)" "x, after asserting its type: the(:int, 5). Type first.";
401    Predicate: "cast" "cast(type, x)" "x checked against, or narrowed to, the type a keyword names: cast(:float, 5) is 5.0. Type first.";
402    Predicate: "foreign?" "foreign?(x)" "Whether x is an opaque value a host handed in.";
403    Predicate: "promise?" "promise?(x)" "Whether x is a delayed value.";
404    Predicate: "chan?" "chan?(x)" "Whether x is a channel.";
405    Predicate: "go?" "go?(x)" "Whether x is a fiber.";
406    // ── errors ────────────────────────────────────────────────────────────
407    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.";
408    Error: "error" "error(kind, message[, data])" "An error VALUE, raising nothing. throw(error(k, m)) is written raise(k, m) (B0037).";
409    Error: "throw" "throw(err)" "Raise any value. Written raise(err) (B0037).";
410    Error: "error?" "error?(x)" "Whether x is an error value; true inside a catch handler.";
411    Error: "error_message" "error_message(e)" "The message of an error value. Written e.message (B0036).";
412    Error: "error_kind" "error_kind(e)" "The kind of an error value, a keyword. Written e.kind (B0036).";
413    Error: "gensym" "gensym([prefix])" "A fresh unique symbol, for macros that need a private name.";
414    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.";
415    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.";
416    // ── JSON ──────────────────────────────────────────────────────────────
417    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.";
418    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.";
419    Json: "json_stringify" "json_stringify(v)" "Compact JSON text, a map's keys sorted, a keyword as its name.";
420    Json: "json_get" "json_get(obj, key)" "The field of a parsed JSON object, or nil. Raises when obj is not an object.";
421    Json: "json_get_or" "json_get_or(obj, key, default)" "The field of a parsed JSON object, or default.";
422    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.";
423    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.";
424    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.";
425    // ── files ─────────────────────────────────────────────────────────────
426    File: "read_file" "read_file(path)" "The file's text. Raises when it cannot be read; shisutemu's read_or(path, default) does not.";
427    File: "write_file" "write_file(path, text)" "Replace the file's contents with text; nil.";
428    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).";
429    File: "append_file" "append_file(path, text)" "Add text to the end of the file, creating it; nil.";
430    File: "rename_file" "rename_file(from, to)" "Move a file, atomically on one filesystem: write a temporary beside to, then rename.";
431    File: "rm" "rm(path)" "Delete a file; raises when it is absent.";
432    File: "rm_rf" "rm_rf(path)" "Delete a file or a directory tree; nil when there is nothing.";
433    File: "mkdir" "mkdir(path)" "Create one directory; nil if it exists.";
434    File: "mkdir_p" "mkdir_p(path)" "Create a directory and its parents.";
435    File: "path_exists" "path_exists(path)" "Whether anything is at path.";
436    File: "is_file?" "is_file?(path)" "Whether path is a regular file.";
437    File: "is_dir?" "is_dir?(path)" "Whether path is a directory.";
438    File: "file_size" "file_size(path)" "The size in bytes.";
439    File: "file_mtime_ms" "file_mtime_ms(path)" "The last modification time, in Unix milliseconds.";
440    File: "ls" "ls(dir)" "The entries of a directory as full paths, sorted.";
441    File: "walk_dir" "walk_dir(root)" "Every file under root, as paths, in no fixed order: sort before comparing.";
442    File: "glob" "glob(pattern)" "Paths matching * and ** relative to the current directory. An absolute pattern finds nothing; walk_dir and filter instead.";
443    File: "path_join" "path_join(part, more...)" "The parts joined with /.";
444    File: "path_basename" "path_basename(path)" "The last component: \"b.txt\" of \"a/b.txt\".";
445    File: "path_dirname" "path_dirname(path)" "Everything before the last component.";
446    File: "path_extension" "path_extension(path)" "The extension without its dot, or nil.";
447    File: "cwd" "cwd" "The current directory.";
448    // ── processes ─────────────────────────────────────────────────────────
449    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.";
450    Process: "exec_check" "exec_check(cmd, args...)" "Run a command, no shell; its exit status as an Int. Output passes through.";
451    Process: "exec_ok?" "exec_ok?(cmd, args...)" "Whether a command exits 0.";
452    Process: "exec_with_stdin" "exec_with_stdin(input, cmd, args...)" "exec_capture, with input written to the command's stdin. The INPUT comes first.";
453    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.";
454    Process: "exec_with_env" "exec_with_env(env, cmd, args...)" "exec_capture with extra environment variables, given as [[name, value], …].";
455    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.";
456    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.";
457    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.";
458    // ── environment ───────────────────────────────────────────────────────
459    Environment: "getenv" "getenv(name[, default])" "An environment variable, or default (nil when none is given).";
460    Environment: "env_required" "env_required(name)" "An environment variable; raises, naming it, when unset.";
461    Environment: "argv" "argv" "The program's own arguments: what follows the file in blue run file -- a b.";
462    Environment: "argv_get" "argv_get(i[, default])" "One argument by index, or default.";
463    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.";
464    // ── time ──────────────────────────────────────────────────────────────
465    Clock: "now" "now" "Unix time in seconds.";
466    Clock: "now_ms" "now_ms" "Unix time in milliseconds.";
467    Clock: "now_ns" "now_ns" "Unix time in nanoseconds.";
468    Clock: "now_rfc3339" "now_rfc3339" "The current UTC time as RFC 3339 text.";
469    Clock: "elapsed_since" "elapsed_since(start_ns)" "Nanoseconds since start_ns, a value of now_ns.";
470    Clock: "sleep" "sleep(seconds)" "Pause for whole seconds.";
471    Clock: "sleep_ms" "sleep_ms(ms)" "Pause for milliseconds.";
472    // ── network ───────────────────────────────────────────────────────────
473    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.";
474    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.";
475    Network: "nats_inbox" "nats_inbox(conn)" "A fresh reply subject unique to this connection, for nats_subscribe and the reply of nats_publish.";
476    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).";
477    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.";
478    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.";
479    Network: "nats_unsubscribe" "nats_unsubscribe(sub)" "Stop a subscription; messages already queued for it are dropped. nil.";
480    Network: "nats_close" "nats_close(conn)" "Close the connection; every later call on it raises. nil.";
481    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.";
482    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.";
483    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.";
484    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.";
485    Network: "pg_execute" "pg_execute(conn, sql)" "A script of statements, unparameterised: the list of their command tags. Raises naming the first failure.";
486    Network: "pg_close" "pg_close(conn)" "Close the connection; every later call on it raises. nil.";
487    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.";
488    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.";
489    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.";
490    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.";
491    Network: "resp_close" "resp_close(conn)" "Close the connection; every later call on it raises. nil.";
492    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.";
493    Network: "http_listener_port" "http_listener_port(listener)" "The TCP port the listener is bound to.";
494    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.";
495    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.";
496    // ── crypto ────────────────────────────────────────────────────────────
497    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.";
498    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.";
499    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.";
500    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.";
501    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.";
502    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.";
503    Crypto: "ed25519_sign" "ed25519_sign(secret_hex, message)" "The Ed25519 signature of a message, in hex.";
504    Crypto: "ed25519_verify" "ed25519_verify(public_hex, message, signature_hex)" "Whether the signature is valid.";
505    // ── output ────────────────────────────────────────────────────────────
506    Output: "write_stdout" "write_stdout(text)" "Write text to stdout exactly, no newline added. The way a command prints.";
507    Output: "write_stderr" "write_stderr(text)" "Write text to stderr exactly.";
508    Output: "println" "println(xs...)" "Print values with a newline, strings with their quotes. For output a person reads, use write_stdout.";
509    Output: "print" "print(x)" "Print a value and a newline, a string with its quotes.";
510    Output: "display" "display(x)" "Print a value's Lisp rendering, no newline.";
511    Output: "newline" "newline" "Print a newline.";
512    // ── delayed values ────────────────────────────────────────────────────
513    Lazy: "force" "force(p)" "The value of a promise, computed once; a non-promise is returned as is.";
514    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.";
515    Lazy: "realize" "realize(lazy)" "A finite tatara-lisp lazy sequence forced into a list. On anything built from cycle it does not terminate.";
516    // ── concurrency ───────────────────────────────────────────────────────
517    Concurrency: "chan" "chan([capacity])" "A tatara-lisp channel. Its put and take words (>! and <!) have no blue spelling.";
518    Concurrency: "close!" "close!(ch)" "Close a channel.";
519    Concurrency: "drain!" "drain!(ch)" "Every value waiting in a channel, as a list.";
520    Concurrency: "go" "go(f)" "A pending fiber for a zero-argument function. Running it (go-run) has no blue spelling.";
521    // ── blue processes ────────────────────────────────────────────────────
522    Actor: "spawn" "spawn(f[, args])" "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.";
523    Actor: "spawn_link" "spawn_link(f[, args])" "spawn, linked to the caller: an abnormal exit of either ends the other, unless it traps exits.";
524    Actor: "spawn_server" "spawn_server(handler, state)" "A process that answers each message m with state = handler(m, state). Its state is what defmigrate transforms.";
525    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.";
526    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.";
527    Actor: "self" "self" "This process's pid. The program itself is 0.";
528    Actor: "alive?" "alive?(pid)" "true while pid is running or waiting.";
529    Actor: "link" "link(pid)" "Link to pid: an abnormal exit of either ends the other. false when pid is not alive.";
530    Actor: "unlink" "unlink(pid)" "Remove a link; true.";
531    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).";
532    Actor: "demonitor" "demonitor(ref)" "Stop a monitor; true.";
533    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.";
534    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.";
535    Actor: "supervisor" "supervisor(strategy, children[, max_restarts])" "Start children (functions, [f, args] pairs, or [:server, handler, state]) 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.";
536    Actor: "children" "children(sup)" "The pids a supervisor started, in order.";
537    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]], …].";
538};
539
540/// The row for a name, if blue describes it.
541#[must_use]
542pub fn doc_of(name: &str) -> Option<&'static NameDoc> {
543    NAMES.iter().find(|d| d.name == name)
544}
545
546#[must_use]
547pub fn signatures() -> &'static blue_lang_check::contracts::Signatures {
548    static SIGNATURES: std::sync::OnceLock<blue_lang_check::contracts::Signatures> =
549        std::sync::OnceLock::new();
550    SIGNATURES.get_or_init(|| {
551        let rows: Vec<blue_lang_check::contracts::BuiltinRow> = NAMES
552            .iter()
553            .map(|d| blue_lang_check::contracts::BuiltinRow {
554                name: d.name,
555                signature: d.signature,
556                kinded: d.topic.kinded(),
557                guard: d.topic == Topic::Predicate && !matches!(d.name, "nil?" | "some?"),
558            })
559            .collect();
560        blue_lang_check::contracts::Signatures::new(&rows)
561    })
562}
563
564impl Topic {
565    #[must_use]
566    pub const fn kinded(self) -> bool {
567        matches!(
568            self,
569            Topic::Number | Topic::Text | Topic::List | Topic::Map | Topic::Function
570        )
571    }
572}