1#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
32pub enum Topic {
33 Form,
35 Lisp,
38 Number,
39 Text,
40 List,
41 Map,
42 Function,
44 Predicate,
46 Error,
47 Json,
48 Nix,
49 Xml,
50 File,
51 Process,
52 Environment,
53 Clock,
54 Network,
56 Device,
58 Crypto,
59 Lazy,
61 Concurrency,
63 Actor,
65 Output,
67 Dispatch,
69}
70
71impl Topic {
72 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 #[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 #[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#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
169pub enum Effect {
170 Host,
173 Output,
175 Concurrency,
177 Actor,
179 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#[derive(Clone, Copy, Debug)]
199pub struct NameDoc {
200 pub name: &'static str,
201 pub topic: Topic,
202 pub signature: &'static str,
204 pub doc: &'static str,
206}
207
208macro_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
216pub const NAMES: &[NameDoc] = names! {
218 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 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 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 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 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 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 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 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 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: "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 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 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: "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 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: "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: "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: "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 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: "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 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#[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}