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