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 Crypto,
57 Lazy,
59 Concurrency,
61 Actor,
63 Output,
65 Dispatch,
67}
68
69impl Topic {
70 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 #[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 #[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#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
162pub enum Effect {
163 Host,
166 Output,
168 Concurrency,
170 Actor,
172 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#[derive(Clone, Copy, Debug)]
192pub struct NameDoc {
193 pub name: &'static str,
194 pub topic: Topic,
195 pub signature: &'static str,
197 pub doc: &'static str,
199}
200
201macro_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
209pub const NAMES: &[NameDoc] = names! {
211 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 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 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 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 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 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 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: "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.";
372 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.";
373 Function: "decorate" "decorate(f, keys-and-values...)" "f with metadata attached; it still calls as f.";
374 Function: "visit" "visit(f, x)" "f(x).";
375 Predicate: "nil?" "nil?(x)" "Whether x is nil (okite D0010). x.nil? says the same and is the spelling blue writes (B0047).";
377 Predicate: "bool?" "bool?(x)" "Whether x is true or false.";
378 Predicate: "boolean?" "boolean?(x)" "The same as bool?.";
379 Predicate: "integer?" "integer?(x)" "Whether x is an Int; integer?(1.0) is false.";
380 Predicate: "float?" "float?(x)" "Whether x is a Float. 6 / 2 is the Int 3, while 7 / 2 is 3.5.";
381 Predicate: "number?" "number?(x)" "Whether x is an Int or a Float.";
382 Predicate: "string?" "string?(x)" "Whether x is a string.";
383 Predicate: "keyword?" "keyword?(x)" "Whether x is a keyword such as :done.";
384 Predicate: "symbol?" "symbol?(x)" "Whether x is a Lisp symbol; false for keywords.";
385 Predicate: "list?" "list?(x)" "Whether x is a list. list?(nil) is true, so test x == nil first.";
386 Predicate: "map?" "map?(x)" "Whether x is a map. A parsed JSON object is not one.";
387 Predicate: "procedure?" "procedure?(x)" "Whether x can be called.";
388 Predicate: "empty?" "empty?(xs)" "Whether xs is nil or has no elements.";
389 Predicate: "null?" "null?(x)" "Whether x is nil or []. The Lisp word; prefer retsu's is_empty.";
390 Predicate: "pair?" "pair?(x)" "Whether x is a non-empty list.";
391 Predicate: "atom?" "atom?(x)" "Whether x is a scalar rather than a list; false for nil.";
392 Predicate: "some?" "some?(x)" "Whether x is not nil.";
393 Predicate: "even?" "even?(n)" "Whether n is even.";
394 Predicate: "odd?" "odd?(n)" "Whether n is odd.";
395 Predicate: "zero?" "zero?(n)" "Whether n is 0.";
396 Predicate: "positive?" "positive?(n)" "Whether n > 0.";
397 Predicate: "negative?" "negative?(n)" "Whether n < 0.";
398 Predicate: "equal?" "equal?(a, b)" "Structural equality; what == lowers to.";
399 Predicate: "eq?" "eq?(a, b)" "Identity: true for the same keyword or string, false for two equal lists. Use ==.";
400 Predicate: "is?" "is?(x, type)" "Whether x has the type named by a keyword: is?(5, :int). Value first.";
401 Predicate: "the" "the(type, x)" "x, after asserting its type: the(:int, 5). Type first.";
402 Predicate: "cast" "cast(type, x)" "x checked against, or narrowed to, the type a keyword names: cast(:float, 5) is 5.0. Type first.";
403 Predicate: "foreign?" "foreign?(x)" "Whether x is an opaque value a host handed in.";
404 Predicate: "promise?" "promise?(x)" "Whether x is a delayed value.";
405 Predicate: "chan?" "chan?(x)" "Whether x is a channel.";
406 Predicate: "go?" "go?(x)" "Whether x is a fiber.";
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.";
409 Error: "error" "error(kind, message[, data])" "An error VALUE, raising nothing. throw(error(k, m)) is written raise(k, m) (B0037).";
410 Error: "throw" "throw(err)" "Raise any value. Written raise(err) (B0037).";
411 Error: "error?" "error?(x)" "Whether x is an error value; true inside a catch handler.";
412 Error: "error_message" "error_message(e)" "The message of an error value. Written e.message (B0036).";
413 Error: "error_kind" "error_kind(e)" "The kind of an error value, a keyword. Written e.kind (B0036).";
414 Error: "gensym" "gensym([prefix])" "A fresh unique symbol, for macros that need a private name.";
415 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.";
416 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.";
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.";
419 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.";
420 Json: "json_stringify" "json_stringify(v)" "Compact JSON text, a map's keys sorted, a keyword as its name.";
421 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.";
422 Json: "json_get" "json_get(obj, key)" "The field of a parsed JSON object, or nil. Raises when obj is not an object.";
423 Json: "json_get_or" "json_get_or(obj, key, default)" "The field of a parsed JSON object, or default.";
424 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.";
425 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.";
426 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.";
427 File: "read_file" "read_file(path)" "The file's text. Raises when it cannot be read; shisutemu's read_or(path, default) does not.";
429 File: "write_file" "write_file(path, text)" "Replace the file's contents with text; nil.";
430 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).";
431 File: "append_file" "append_file(path, text)" "Add text to the end of the file, creating it; nil.";
432 File: "rename_file" "rename_file(from, to)" "Move a file, atomically on one filesystem: write a temporary beside to, then rename.";
433 File: "rm" "rm(path)" "Delete a file; raises when it is absent.";
434 File: "rm_rf" "rm_rf(path)" "Delete a file or a directory tree; nil when there is nothing.";
435 File: "mkdir" "mkdir(path)" "Create one directory; nil if it exists.";
436 File: "mkdir_p" "mkdir_p(path)" "Create a directory and its parents.";
437 File: "path_exists" "path_exists(path)" "Whether anything is at path.";
438 File: "is_file?" "is_file?(path)" "Whether path is a regular file.";
439 File: "is_dir?" "is_dir?(path)" "Whether path is a directory.";
440 File: "file_size" "file_size(path)" "The size in bytes.";
441 File: "file_mtime_ms" "file_mtime_ms(path)" "The last modification time, in Unix milliseconds.";
442 File: "ls" "ls(dir)" "The entries of a directory as full paths, sorted.";
443 File: "walk_dir" "walk_dir(root)" "Every file under root, as paths, in no fixed order: sort before comparing.";
444 File: "glob" "glob(pattern)" "Paths matching * and ** relative to the current directory. An absolute pattern finds nothing; walk_dir and filter instead.";
445 File: "path_join" "path_join(part, more...)" "The parts joined with /.";
446 File: "path_basename" "path_basename(path)" "The last component: \"b.txt\" of \"a/b.txt\".";
447 File: "path_dirname" "path_dirname(path)" "Everything before the last component.";
448 File: "path_extension" "path_extension(path)" "The extension without its dot, or nil.";
449 File: "cwd" "cwd" "The current directory.";
450 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.";
451 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.";
452 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.";
454 Process: "exec_check" "exec_check(cmd, args...)" "Run a command, no shell; its exit status as an Int. Output passes through.";
455 Process: "exec_ok?" "exec_ok?(cmd, args...)" "Whether a command exits 0.";
456 Process: "exec_with_stdin" "exec_with_stdin(input, cmd, args...)" "exec_capture, with input written to the command's stdin. The INPUT comes first.";
457 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.";
458 Process: "exec_with_env" "exec_with_env(env, cmd, args...)" "exec_capture with extra environment variables, given as [[name, value], …].";
459 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.";
460 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.";
461 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.";
462 Environment: "getenv" "getenv(name[, default])" "An environment variable, or default (nil when none is given).";
464 Environment: "env_required" "env_required(name)" "An environment variable; raises, naming it, when unset.";
465 Environment: "argv" "argv" "The program's own arguments: what follows the file in blue run file -- a b.";
466 Environment: "argv_get" "argv_get(i[, default])" "One argument by index, or default.";
467 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.";
468 Clock: "now" "now" "Unix time in seconds.";
470 Clock: "now_ms" "now_ms" "Unix time in milliseconds.";
471 Clock: "now_ns" "now_ns" "Unix time in nanoseconds.";
472 Clock: "now_rfc3339" "now_rfc3339" "The current UTC time as RFC 3339 text.";
473 Clock: "elapsed_since" "elapsed_since(start_ns)" "Nanoseconds since start_ns, a value of now_ns.";
474 Clock: "sleep" "sleep(seconds)" "Pause for whole seconds.";
475 Clock: "sleep_ms" "sleep_ms(ms)" "Pause for milliseconds.";
476 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.";
478 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.";
479 Network: "nats_inbox" "nats_inbox(conn)" "A fresh reply subject unique to this connection, for nats_subscribe and the reply of nats_publish.";
480 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).";
481 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.";
482 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.";
483 Network: "nats_unsubscribe" "nats_unsubscribe(sub)" "Stop a subscription; messages already queued for it are dropped. nil.";
484 Network: "nats_close" "nats_close(conn)" "Close the connection; every later call on it raises. nil.";
485 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.";
486 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.";
487 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.";
488 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.";
489 Network: "pg_execute" "pg_execute(conn, sql)" "A script of statements, unparameterised: the list of their command tags. Raises naming the first failure.";
490 Network: "pg_close" "pg_close(conn)" "Close the connection; every later call on it raises. nil.";
491 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.";
492 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.";
493 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.";
494 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.";
495 Network: "resp_close" "resp_close(conn)" "Close the connection; every later call on it raises. nil.";
496 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.";
497 Network: "http_listener_port" "http_listener_port(listener)" "The TCP port the listener is bound to.";
498 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.";
499 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.";
500 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.";
501 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.";
502 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.";
503 Network: "flash_close" "flash_close(wire)" "Close the wire's Flash channel; the socket closes with its last channel. nil.";
504 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.";
505 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.";
506 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.";
507 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.";
508 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.";
509 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.";
510 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.";
511 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.";
512 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.";
514 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.";
515 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.";
516 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.";
517 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.";
518 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.";
519 Crypto: "ed25519_sign" "ed25519_sign(secret_hex, message)" "The Ed25519 signature of a message, in hex.";
520 Crypto: "ed25519_verify" "ed25519_verify(public_hex, message, signature_hex)" "Whether the signature is valid.";
521 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.";
522 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.";
523 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.";
524 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.";
525 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.";
526 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.";
527 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:}.";
528 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.";
529 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.";
530 Output: "write_stdout" "write_stdout(text)" "Write text to stdout exactly, no newline added. The way a command prints.";
532 Output: "write_stderr" "write_stderr(text)" "Write text to stderr exactly.";
533 Output: "println" "println(xs...)" "Print values with a newline, strings with their quotes. For output a person reads, use write_stdout.";
534 Output: "print" "print(x)" "Print a value and a newline, a string with its quotes.";
535 Output: "display" "display(x)" "Print a value's Lisp rendering, no newline.";
536 Output: "newline" "newline" "Print a newline.";
537 Lazy: "force" "force(p)" "The value of a promise, computed once; a non-promise is returned as is.";
539 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.";
540 Lazy: "realize" "realize(lazy)" "A finite tatara-lisp lazy sequence forced into a list. On anything built from cycle it does not terminate.";
541 Concurrency: "chan" "chan([capacity])" "A tatara-lisp channel. Its put and take words (>! and <!) have no blue spelling.";
543 Concurrency: "close!" "close!(ch)" "Close a channel.";
544 Concurrency: "drain!" "drain!(ch)" "Every value waiting in a channel, as a list.";
545 Concurrency: "go" "go(f)" "A pending fiber for a zero-argument function. Running it (go-run) has no blue spelling.";
546 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) while the node waits for it, and 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.";
548 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.";
549 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.";
550 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.";
551 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.";
552 Actor: "self" "self" "This process's pid. The program itself is 0.";
553 Actor: "alive?" "alive?(pid)" "true while pid is running or waiting.";
554 Actor: "link" "link(pid)" "Link to pid: an abnormal exit of either ends the other. false when pid is not alive.";
555 Actor: "unlink" "unlink(pid)" "Remove a link; true.";
556 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).";
557 Actor: "demonitor" "demonitor(ref)" "Stop a monitor; true.";
558 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.";
559 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.";
560 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.";
561 Actor: "children" "children(sup)" "The pids a supervisor started, in order.";
562 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]], …].";
563 Actor: "evict" "evict(pid, wire, peer)" "Move server pid off this node at its next message boundary: its code, state, scheduling class and waiting messages 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]). 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; a plain, linked or supervised process is refused, and so is an image the send ring has no room for, which leaves the process running here. Needs the comm_scavenger capability.";
564 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, scheduling class and waiting messages, and returns its new pid; nil when no whole image came. A frame of the image that is malformed, or an image whose bytes hash to another name, raises :decode_error. 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.";
565};
566
567#[must_use]
569pub fn doc_of(name: &str) -> Option<&'static NameDoc> {
570 NAMES.iter().find(|d| d.name == name)
571}
572
573#[must_use]
574pub fn signatures() -> &'static blue_lang_check::contracts::Signatures {
575 static SIGNATURES: std::sync::OnceLock<blue_lang_check::contracts::Signatures> =
576 std::sync::OnceLock::new();
577 SIGNATURES.get_or_init(|| {
578 let rows: Vec<blue_lang_check::contracts::BuiltinRow> = NAMES
579 .iter()
580 .map(|d| blue_lang_check::contracts::BuiltinRow {
581 name: d.name,
582 signature: d.signature,
583 kinded: d.topic.kinded(),
584 guard: d.topic == Topic::Predicate && !matches!(d.name, "nil?" | "some?"),
585 })
586 .collect();
587 blue_lang_check::contracts::Signatures::new(&rows)
588 })
589}
590
591impl Topic {
592 #[must_use]
593 pub const fn kinded(self) -> bool {
594 matches!(
595 self,
596 Topic::Number | Topic::Text | Topic::List | Topic::Map | Topic::Function
597 )
598 }
599}