DUST
Table of Contents
- 1. Language
- 2. Core Functions
- 3. Comparison Functions
- 4. Function Utilities
- 5. Boolean predicates
- 6. Collection Functions
- 6.1. List functions: lists/
- 6.1.1. lists/mapcat
- 6.1.2. lists/keep
- 6.1.3. lists/nth
- 6.1.4. lists/second
- 6.1.5. lists/last
- 6.1.6. lists/append
- 6.1.7. lists/concat
- 6.1.8. lists/interpose
- 6.1.9. lists/interleave
- 6.1.10. lists/flatten
- 6.1.11. lists/slice
- 6.1.12. lists/take
- 6.1.13. lists/take-while
- 6.1.14. lists/drop
- 6.1.15. lists/drop-while
- 6.1.16. lists/partition
- 6.1.17. lists/reverse
- 6.1.18. lists/filter
- 6.1.19. lists/any?
- 6.1.20. lists/every?
- 6.1.21. lists/some
- 6.1.22. lists/find-first
- 6.1.23. lists/range
- 6.1.24. lists/repeat
- 6.1.25. lists/sort-by
- 6.1.26. lists/partition-by
- 6.1.27. lists/group-by
- 6.1.28. lists/frequencies
- 6.2. Map functions: maps/
- 6.3. Collection paths: collections/
- 6.1. List functions: lists/
- 7. Hash Functions
- 8. Random Functions
- 9. String Functions
- 9.1. String functions
- 9.1.1. strings/case
- 9.1.2. strings/trim
- 9.1.3. strings/split
- 9.1.4. strings/join
- 9.1.5. strings/replace
- 9.1.6. strings/substring
- 9.1.7. strings/starts-with?
- 9.1.8. strings/ends-with?
- 9.1.9. strings/includes?
- 9.1.10. strings/split-lines
- 9.1.11. strings/index-of
- 9.1.12. strings/last-index-of
- 9.1.13. strings/replace-first
- 9.1.14. strings/blank?
- 9.1.15. String templates
- 9.1.16. strings/escape
- 9.1.17. strings/unescape
- 9.1.18. strings/normalize
- 9.1.19. strings/from-code-points
- 9.2. strings/humanize
- 9.1. String functions
- 10. Regular Expression Functions
- 11. Mathematics Functions
- 11.1. Math functions
- 11.1.1. math/pi
- 11.1.2. math/tau
- 11.1.3. math/deg2rad
- 11.1.4. math/rad2deg
- 11.1.5. math/e
- 11.1.6. math/floor
- 11.1.7. math/ceil
- 11.1.8. math/round
- 11.1.9. math/rint
- 11.1.10. math/trunc
- 11.1.11. math/pow
- 11.1.12. math/sqrt
- 11.1.13. math/cbrt
- 11.1.14. math/hypot
- 11.1.15. math/exp
- 11.1.16. math/exp2
- 11.1.17. math/expm1
- 11.1.18. math/log
- 11.1.19. math/log2
- 11.1.20. math/log10
- 11.1.21. math/log1p
- 11.1.22. math/sin
- 11.1.23. math/cos
- 11.1.24. math/tan
- 11.1.25. math/asin
- 11.1.26. math/acos
- 11.1.27. math/atan
- 11.1.28. math/atan2
- 11.1.29. math/sinh
- 11.1.30. math/cosh
- 11.1.31. math/tanh
- 11.1.32. math/asinh
- 11.1.33. math/acosh
- 11.1.34. math/atanh
- 11.1.35. math/radians
- 11.1.36. math/degrees
- 11.1.37. math/signum
- 11.1.38. math/copy-sign
- 11.1.39. math/floor-div
- 11.1.40. math/floor-mod
- 11.1.41. math/abs
- 11.1.42. math/sign
- 11.1.43. math/lerp
- 11.1.44. math/inverse-lerp
- 11.1.45. math/remap
- 11.1.46. math/zero?
- 11.1.47. math/odd?
- 11.1.48. math/even?
- 11.1.49. math/min
- 11.1.50. math/max
- 11.1.51. math/clamp
- 11.1. Math functions
- 12. Time Functions
- Appendix A: Function Index
Work in progress. This specification is incomplete and subject to change.
DUST (Data Universal S-expression Toolkit) is a small, high-performance, game‑engine‑inspired Lisp dialect designed for embedded information processing, data manipulation, and business logic.
DUST source files use the .dust extension. DUST extends RON with syntax for expressions and evaluation rules, while retaining a JSON representation of every source form.
DUST is developed under the StarFederation nonprofit. [?]
This reference covers syntax, evaluation, special forms, and all standard functions. The Function Index lists every callable alphabetically. The Epsilon page covers the engine's command line, formatter, REPL, and evaluation costs.
(let [total (fn total [items discount] (reduce :+ 0.0 (map (fn [{quantity quantity unitPrice unitPrice}] (* (float (* .quantity .unitPrice)) (- 1.0 .discount))) .items)))] [(total [{quantity 3 unitPrice 20} {quantity 2 unitPrice 15}] 0.1) (total [{quantity 2 unitPrice 15}] 0.0) (total [] 0.5)])
(let [total (fn total [items discount] (reduce :+ 0.0 (map (fn [{quantity quantity unitPrice unitPrice}] (* (float (* .quantity .unitPrice)) (- 1.0 .discount))) .items)))] [(total [{quantity 3 unitPrice 20} {quantity 2 unitPrice 15}] 0.1) (total [{quantity 2 unitPrice 15}] 0.0) (total [] 0.5)])
81.0 30.0 0.0
1. Language ↑
1.1. Syntax ↑
The .dust format extends .ron. References, invocations, quotation shorthand, and mute shorthand belong exclusively to DUST. Their explicit tagged-element representations are ordinary data in RON and JSON.
It is conventional for Lisps to have a reader, the system that ingests plain text and outputs abstract-syntax-trees used by the compiler and macros. DUST doesn't have a reader in the traditional Lisp sense, rather it has a parser1.
DUST builds on RON’s data notation and adds its own expression parser. Parsing constructs forms without evaluating them.
Parentheses delimit invocations, not literal lists or quotation forms.
[square brackets] are also used to delimit lists, used for literal
data structures and in specials and macros to delimit where new bindings
are introduced, such as argument lists and let bindings.
{curly braces} delimit maps containing key-value pairs and optional
tags.
Ordinary members pair a string key with a value.
Tags supply boolean-valued members using RON's shorthand.
Map keys must be strings.
Map values can be of any type.
1.1.1. Strings ↑
DUST uses RON string syntax: bare strings, double-quoted strings, and fences of two or more double quotes. All produce strings, not quotation forms. The examples below show the RON data produced by parsing DUST source.
In value position, a bare token matching a number, null, true, or false denotes that value.
Quote it to make a string.
Other bare tokens denote strings, except where DUST reserves a prefix for expression syntax.
A leading # introduces a tagged element. In value position, a leading .
introduces a local reference, a leading : a constant reference, and a leading
/ a JSON Pointer. Leading `, ~,
,, and ,@ introduce quotation forms. A leading \ mutes the next
complete form. Quote these characters when they are meant to begin a string. Apostrophes and printable Unicode characters,
including emoji, are ordinary string characters:
[hello ".-#1" don't true false null 42 -1.5 "true" "false" "null" "42" "-1.5" nil "#name" "^name" .name 🙂 "" {true false}]
hello ".-#1" don't true false null 42 -1.5 "true" "false" "null" "42" "-1.5" nil "#name" ^name .name 🙂 "" <!true>
[hello .-#1 don't true false null 42 -1.5 "true" "false" "null" "42" "-1.5" nil "#name" ^name #local name 🙂 "" <!true>]
A square bracket alone on its opening line starts a line-oriented list. A bare
string item occupies one line and may contain spaces; numeric and other scalar
literals keep their types. If the first item follows [ on the opening line,
whitespace separates items on every line; quote strings containing spaces in
that packed form:
[ Ada Lovelace Grace Hopper ]
"Ada Lovelace" "Grace Hopper"
[ Ada Lovelace Grace Hopper]
Ordinary double-quoted strings use JSON escapes. Fenced strings allow double quotes inside without escaping them, but backslashes and control characters still use escapes. Unicode escapes decode to their characters:
["say \"hello\"" ""C:\\tmp\\notes.txt"" "\u03b5" "\u0000"]
""say "hello""" C:\tmp\notes.txt ε "\u0000"
[ say "hello" C:\tmp\notes.txt ε "\u0000"]
A fence of three or more quotes followed only by whitespace and a newline starts a multiline string. The newline after the opening fence and the newline before the closing fence are not part of the string. An extra blank line before the closing fence preserves a trailing newline. A single-line fence and a multiline fence can therefore contain the same string:
[ ""hello world"" """ hello world """ """ first line second line """ ]
"hello world" "hello world" """ first line second line """
[ hello world hello world """ first line second line """ ]
A quoted ., :, or / is a string; an unquoted one starts a reference or
pointer. A caret has no special meaning:
["^name" "^x" .name :name "^" .name]
^name ^x .name :name ^ .name
[^name ^x #local name #const name ^ #local name]
Outside map-key position, a token beginning with . or : denotes a
reference. Outside map-key and direct-callee positions, a token
that starts with / is a pointer. Put quotes around the token to make it a string.
Binding forms introduce names as strings, conventionally written bare.
A direct callee is a name encoded as a string in the first
element of #invoke, without a #local or #const wrapper.
In this position, surrounding quote characters belong to the name.
A computed callee is an expression in the first element of #invoke,
including a reference such as (.f 1) or (:strings/case upper hi).
Elsewhere, bare names and quoted strings denote text.
Whitespace and layout follow RON, except for DUST's form separation rules.
1.2. Source notation ↑
1.2.1. Documents and form separation ↑
A DUST file holds one or more forms. A file containing exactly one form is that form. Two or more forms, separated by whitespace including newlines, make an implied root list; its outer square brackets may be written or omitted. A root string containing spaces must be quoted. Unlike RON, whose default document is an implied outer object, DUST has no implied root map: a root map is written with braces.
Canonical DUST omits the root list's brackets when the list has two or more forms and its first non-comment form is not a literal list:
(+ 1 2)
(* 3 4)
Root forms are separated by whitespace, not lines, so a root string containing
spaces is quoted, as in "two words".
Explicit outer brackets around these forms are also accepted and produce the same list, but are not canonical.
Otherwise the brackets are kept, so a one-element list [x] stays distinct
from the form x, and [[1 2]] stays distinct from [1 2].
Once an earlier form establishes an implied root list, later literal lists
need only their own brackets.
A file is evaluated one top-level form at a time, from first to last, so a
form can use the top-level locals that earlier forms define. Its value is
the list of the forms' values: (+ 1 2) (* 3 4) evaluates to [3 12], and a
file with one form evaluates to that form's value. An empty file is an empty
list. Read for evaluation, as by the Epsilon REPL, a written list is always
one form, so [1 2] followed by [3] is two forms, and each form can have a
depth of 256.
DUST comments start with ; at the start of a line or after whitespace and
continue to the end of that line. A semicolon inside a quoted string or inside
an unbroken token is text. RON uses the same comment syntax. A document must not
mix ; comments with an explicit $comment member.
A comment belongs to the value occurrence it describes. A comment immediately before an item belongs to that item. A blank line between the comment and item attaches the comment to the enclosing collection. A comment after a value on the same line belongs to the end of that occurrence.
For example, this file evaluates to 3:
; Root documentation
(+ 1 2)
In explicit comment round-trip mode, its JSON representation is:
[
{"$comment": "{\"root\":\"scalar\",\"start\":{\"\":[\"Root documentation\"]}}"},
{"#invoke": ["+", 1, 2]}
]
The marker preserves the comment and is not an element of the value.
Collection opening and closing comments are distinct from comments on the
collection occurrence. epsilon convert --comment-roundtrip preserves these
placements when converting through JSON.
In expression positions, unquoted (), [], and {} delimit forms and end bare strings, reference names, and direct-callee names.
In packed lists and invocations, these delimiters separate forms without whitespace.
[[foo(bar)] (foo(bar)) [foo[bar]{}(bar)baz] [[.foo(bar)][(bar):foo]]]
[[foo (bar)] (foo (bar)) [foo [bar] {} (bar) baz] [[.foo (bar)] [(bar) :foo]]]
[[foo #invoke [bar]] #invoke [foo #invoke [bar]] [foo [bar] {} #invoke [bar] baz] [[#local foo #invoke [bar]] [#invoke [bar] #const foo]]]
Each line-oriented list item or map member value contains one form.
Bare line strings can contain spaces, but end at expression delimiters.
foo(bar) contains two forms. It may supply consecutive root forms,
but is invalid as one line-oriented list item or map member value.
"foo(bar)" is one string.
Map keys use RON's token rules, including embedded brackets and parentheses, and require whitespace before their values. Angle brackets delimit tag groups only in map-member position.
[{items[0] 1 foo(bar) 2 .name 3}]
[{.name 3 foo(bar) 2 items[0] 1}]
[{.name 3 foo(bar) 2 items[0] 1}]
A key that begins with ) is quoted, since a line that begins with ) closes a form:
{"(a" b ")" 1 ")x" 2 "x)" 3 "(" 4 k ["a <b" "c" "a<b"]}
{( 4 (a b ")" 1 ")x" 2 k [ "a <b" c a<b] x) 3}
( 4 (a b ) 1 )x 2 k [ "a <b" c a<b] x) 3
1.2.2. Nesting depth ↑
Forms can nest to a maximum depth of 256. The parser and the evaluator count depth with the same rules:
- Each list, map, invocation, tag, quotation prefix, and mute prefix adds one level.
A scalar adds no level, so
[[1]]has a depth of 2. - An invocation is one level, because its list is a part of the invocation.
- A list that is the payload of a tag or of a prefix is a part of it and adds no level.
Thus
(f x),#invoke [f x],{#invoke [f x]}, and`[x]each have a depth of 1. - A form in a string template or in a JSON pointer is below the template or the pointer.
- A root list adds one level, written or implied. Thus each form of a file with two or more forms can have a depth of 255. A file with one form has no root list, and that form can have a depth of 256.
The parser rejects a form that is deeper than 256 levels.
This is a resource limit, not a syntax error.
The result is #err <limit resource>, with a message that gives the depth limit:
#err {<limit resource>
column 257
line 1
message "DUST parse: nesting exceeds the depth limit of 256"}
Other parser errors produce #err <invalid value>.
RON documents have the same depth limit and count levels with the same rules.
1.2.3. Internal limits ↑
An implementation can have internal limits that are lower than the limits of the language.
These limits are high-water marks of the implementation, not of the language.
A valid program can be outside an internal limit.
For example, a valid program can have expressions that nest too deeply for the implementation,
or a function that has too many names.
An implementation can find a limit before evaluation starts or during evaluation.
For example, calls that nest too deeply during evaluation are outside the recursion limit.
The result of such a program is #err <limit internal KIND>, where KIND names the limit.
The message starts with "program exceeds an internal limit:" and names the limit.
| Tags | Limit | Message |
|---|---|---|
<internal limit nesting> |
Nesting of expressions | program exceeds an internal limit: expressions nest too deeply |
<internal limit locals> |
Names in one function | program exceeds an internal limit: too many names in one function |
<captures internal limit> |
Names that one function captures | program exceeds an internal limit: a function captures too many names |
<internal limit stack> |
Working slots of one function | program exceeds an internal limit: a function needs too many working slots |
<internal limit size> |
Size of one function body | program exceeds an internal limit: a function body is too large |
<constants internal limit> |
Constants in one function | program exceeds an internal limit: a function has too many constants |
<internal limit recursion> |
Nesting of calls during evaluation | program exceeds an internal limit: calls nest too deeply |
A different implementation, or a later version, can accept the same program. Thus a result outside an internal limit is not a part of the language contract.
[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ 1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
#err {<internal limit nesting> message "program exceeds an internal limit: expressions nest too deeply"}
(let [count-up (fn count-up [n] (+ 1 (count-up (+ .n 1))))] (count-up 0))
#err {<internal limit recursion> message "program exceeds an internal limit: calls nest too deeply"}
1.2.4. References ↑
Outside map-key position, . followed by a nonempty name produces a #local
tagged element, and : followed by a nonempty name produces a #const tagged
element. The two are the same in the first position of an invocation, where they
make a computed callee.
Names follow RON's packed bare-token rules and DUST's form separation rules above.
Numeric, boolean, and null spellings are names after the prefix, so .5 is
#local "5", not a number.
An empty name, or a name that begins with a quote, ., or :, is invalid:
., :, ..., ::x, and ."x" are syntax errors. Quote such text to write a
string. A . or : after the first character of a bare string is text, as
in a.b or a:b.
{var .name const :name punctuation (fn? :+) qualified-var .user/name qualified-const :math/pi colon .a:b dot :a.b literal-names [.42 .-1 .1.5 .true .false .null] caret-name .^+ nested [.x {value :y}]}
{caret-name .^+ colon .a:b const :name dot :a.b literal-names [.42 .-1 .1.5 .true .false .null] nested [.x {value :y}] punctuation (fn? :+) qualified-const :math/pi qualified-var .user/name var .name}
caret-name #local ^+ colon #local a:b const #const name dot #const a.b literal-names [#local "42" #local "-1" #local "1.5" #local "true" #local "false" #local "null"] nested [#local x {value #const y}] punctuation #invoke [fn? #const +] qualified-const #const math/pi qualified-var #local user/name var #local name
#err <name unbound>
(let [add :+] [(add 0 0) (add 1 2) (add -4 10)])
(let [add :+] [(add 0 0) (add 1 2) (add -4 10)])
#invoke [let [add #const +] [#invoke [add 0 0] #invoke [add 1 2] #invoke [add -4 10]]]
0 3 6
Parsing constructs the reference without looking up its name. Binding and parameter names in DUST are ordinary strings, conventionally written bare, rather than references. Parsing does not interpret binding positions.
1.2.5. String Template Literals ↑
A string template is a quoted or fenced string prefixed with ~, the
quasiquote prefix. Inside it, , followed by a form embeds that form, as
unquote does in a quasiquoted list. A string without ~ is always literal, so
",.name" and "{{x}}" are plain text.
(let [name Ada] ~"Hello ,.name") ; => "Hello Ada"
(let [subtotal 10 tax 2] ~"Total: ,(+ .subtotal .tax)") ; => "Total: 12"
~"Pi is ,:math/pi" ; => "Pi is 3.141592653589793"
(let [name Ada]
~"""
Hello ,.name
Sum ,(+ 1 2)
""") ; => "Hello Ada\nSum 3"
An embedded form in parentheses, brackets, braces, or quotes ends at its closing delimiter. Any other form, such as a reference, reads a full name and ends at whitespace, a comma, a quote, or a bracket. A comma that ends it is consumed, so a reference can be followed directly by text:
(let [name Ada] ~"Hi ,.name there") ; => "Hi Ada there"
(let [name Ada] ~"Hi ,.name,!") ; => "Hi Ada!"
(let [foo! bang] ~"Hi ,.foo!") ; => "Hi bang"
(let [name Ada] ~"Hi ,.name,, there") ; => "Hi Ada, there"
A comma followed by whitespace or the closing quote is text, and ,, writes a
literal comma anywhere:
~"a, b" ; => "a, b"
~"x,,y" ; => "x,y"
Embedded forms may contain quotes, as in ~"a ,(str "x") b". A quoted string
cannot be embedded directly in a one-line template, because ," before the
closing quote is literal text and a quote otherwise ends the template; write
,(str "x") or use a fenced ~""" template. Only a string written directly
after ~ is a template; a string nested inside a quasiquoted list stays
literal, so ~[a ,.x "b ,.x"] keeps "b ,.x".
The resulting template value keeps the literal pieces and evaluated values
separate, so template functions can inspect them or
escape each value. A printed or exported template value is its rendered string,
the same text str produces. Printing loses the parts: reading the printed
result back gives an ordinary string, not a template. A quoted template is a
form rather than an evaluated value, so it prints as a template literal.
In RON, the #template tagged representation contains a list of literal
strings and a list of forms. The first list has one more item than the second.
For example, this DUST template:
~"hello-,.name"
has this RON representation:
#template [[hello- ""] [#local name]]
DUST formatting writes the template literal when the tagged representation
can be expressed without changing its data. It always writes a one-line
~"…" literal, escaping line breaks as \n, so a multiline ~""" template
is reformatted on one line. A template that embeds a string value that is not
a bare token keeps the explicit #template notation.
See string templates for construction, inspection, and rendering.
1.2.6. JSON Pointers ↑
RFC 6901 defines JSON Pointer as a string syntax for identifying a value
within a JSON document. Its reference tokens are separated by /; within a
token, ~1 represents / and ~0 represents ~. The empty string refers
to the root, while / refers to a member with an empty name.
DUST uses that syntax for pointer text and adds pointer values and embedded forms. A pointer value can be used with DUST maps and lists. DUST distinguishes integer index segments from string key segments; numeric strings with valid index spelling become indices. Embedded forms and DUST's backslash escapes below are DUST extensions, not RFC 6901 pointer text.
(get {a/b 1} /a~1b) ; 1
(get {items [zero one]} /items/1) ; one
(get {"" 7} /) ; 7
A slash-prefixed value constructs a pointer. Pass it to a collection path
function to read or update data. In callee position, / is division; a map
literal key beginning with / is a string.
(get {a 1} /a) ; 1
(/ 10 2) ; 5.0
(get {/a 1} "/a") ; 1
A pointer has slash-separated segments:
(get {"" 7} /) ; 7
(get {"" {a 1}} //a) ; 1
(get {a {"" 2}} /a/) ; 2
(get {a 1} (jsonptr "")) ; {a 1}
/ selects an empty key; it does not select the root. Use (jsonptr "")
for the root pointer.
Segments can contain embedded forms. As in a string template,
, followed by a form embeds it, and a comma that ends a token form is
consumed. A pointer needs no ~: a bare pointer cannot otherwise contain a comma.
(let [key a] (get {a 1} /,.key)) ; 1
(let [id 2] (get {user-3 ok} /user-,(+ 1 .id))) ; ok
(let [id 7] /users/,.id,/name) ; /users/7/name
Names can contain /, so /users/,.id/name reads the reference .id/name;
the closing comma in /users/,.id,/name ends .id before the next segment.
An embedded segment value must be a string or a nonnegative integer.
The RFC escapes ~1 and ~0 select keys containing / and ~:
(get {a/b 1} /a~1b) ; 1
(get {m~n 2} /m~0n) ; 2
DUST also accepts \{, \}, and \\ for literal braces and backslashes,
and ,, for a literal comma. Other tilde or backslash escapes are syntax
errors. Parentheses, caret, opening braces, and double quote are literal
pointer text; an unmatched closing parenthesis ends the pointer token.
Whitespace, square brackets, a closing brace, or a comma followed by whitespace
or a closer also ends the token.
RON represents a literal pointer as #jsonptr with a string payload. A
pointer containing embeds has a list payload. For example, these DUST pointers:
/a/0
/3/,"3"
/foo/user-,(+ 1 .id)/,.k,/0
have these RON representations:
[#jsonptr /a/0 #jsonptr /3/3 #jsonptr [foo [user- #invoke [+ 1 #local id]] #local k 0]]
Formatting the third pointer as DUST writes its embedded forms in the pointer
literal rather than the #jsonptr representation.
In the second example, both segments are indices. Map lookup converts an index to its decimal string key. Quotation, mute, and unevaluated parts of quasiquotation preserve tagged data; unquote follows the usual quasiquote rules.
(jsonptr? /) (str /) (str //a) (str /a/) (str /a~1b/x~0y) (str /f(x)) (let [k name] (str /users/,.k)) (let [n 10] (str /user-,(+ .n 1))) (let [k "a/b"] (str /,.k)) (let [k "x~y"] (str /,.k)) (let [p /3/,"3"] (get [null null null {3 found}] .p)) (let [p /3/,(int "3")] (get [null null null [a b c found]] .p)) /,null /,true (count /a/b/0) (first /a/b/0) (str (rest /a/b/0)) (= /a/0 (jsonptr "/a/0")) (let [x 3] (str /foo/,.x)) (get [a b c d] /,"3") (get [a b c d] /,"\u0033") (let [n "3"] (get [a b c d] /,.n)) (let [n "3"] (get {3 yes} /,.n)) (let [n "03"] (get {03 yes} /,.n)) (let [n "3"] (= /,.n /3))
(jsonptr? /) (str /) (str //a) (str /a/) (str /a~1b/x~0y) (str /f(x)) (let [k name] (str /users/,.k)) (let [n 10] (str /user-,(+ .n 1))) (let [k a/b] (str /,.k)) (let [k x~y] (str /,.k)) (let [p /3/3] (get [null null null {3 found}] .p)) (let [p /3/,(int "3")] (get [null null null [a b c found]] .p)) /,null /,true (count /a/b/0) (first /a/b/0) (str (rest /a/b/0)) (= /a/0 (jsonptr "/a/0")) (let [x 3] (str /foo/,.x)) (get [a b c d] /3) (get [a b c d] /3) (let [n "3"] (get [a b c d] /,.n)) (let [n "3"] (get {3 yes} /,.n)) (let [n 03] (get {03 yes} /,.n)) (let [n "3"] (= /,.n /3))
true "/" "//a" "/a/" "/a~1b/x~0y" "/f(x)" "/users/name" "/user-11" "/a~1b" "/x~0y" found found #err {<mismatch type> message "pointer segments must be strings or integers"} #err {<mismatch type> message "pointer segments must be strings or integers"} 3 a "/b/0" true "/foo/3" d d d yes yes true
(let [p /3/,"3"] [.p (get [null null null {3 yes}] .p) (str .p)]) (let [name Ada] (str /,~"hello-,.name")) (str /,~"inner ,(str "x")")
(let [p /3/3] [.p (get [null null null {3 yes}] .p) (str .p)]) (let [name Ada] (str /,~"hello-,.name")) (str /,~"inner ,(str x)")
[ [/3/3 yes "/3/3"] "/hello-Ada" "/inner x"]
1.2.7. Invocations ↑
Parentheses produce a #invoke tagged element whose payload is a list.
The first element is the callee, and the remaining elements are argument forms.
A direct callee is a name encoded as a string, without a #local or #const wrapper.
In this position, surrounding quote characters belong to the name.
This differs from string parsing in argument position.
A callee that begins with . or : is a reference, and therefore a computed callee.
(upper hello)
#invoke [upper hello]
#cases [{bindings {upper (fn [x] .x)} output hello}]
("upper" hello)
#invoke [""" "upper" """ hello]
#cases [{bindings {""\"upper""" (fn [x] .x)} output hello}]
("+" 1 2)
#invoke [""" "+" """ 1 2]
#cases [{bindings {""\"+""" (fn [a b] (+ .a .b))} output 3}]
(let [twice (fn [x] (* 2 .x))] (.twice 21))
(let [twice (fn [x] (* 2 .x))] (.twice 21))
#invoke [let [twice #invoke [fn [x] #invoke [* 2 #local x]]] #invoke [#local twice 21]]
(:upper hello)
#invoke [#const upper hello]
#cases [{bindings {upper (fn [x] .x)} output hello}]
(f :x)
#invoke [f #const x]
#cases [{bindings {f (fn [value] .value) x Ada} output Ada}]
A computed callee is encoded as the first form in #invoke.
For example:
The parser does not resolve invocation names, check whether a callee produces a function, or evaluate arguments.
Unknown names and expressions such as (/ 1 0) are valid parser inputs.
Their evaluation belongs to DUST.
An empty invocation, (), parses as #invoke [].
Parsing does not require a callee; an empty invocation has no function to evaluate.
Whitespace separates forms throughout an invocation, including across newlines,
even when the callee begins on a later line. A bare argument containing spaces
must be quoted. Lists retain RON's packed and line-oriented rules in every
position, including binding and parameter lists. The parser does not interpret
special-form names or binding positions.
(let [ first name second name ] [first name])
(let [ first name second name] [first name])
#invoke [let [ first name second name] [first name]]
1.2.8. Quotation forms ↑
The four prefixes apply to the immediately following form and parse to a tagged element. The prefix and its form are adjacent; quote or fence a string beginning with one of these characters when it is meant as text.
| Prefix | Explicit tagged notation |
|---|---|
`form |
#quote form |
~form |
#quasiquote form |
,form |
#unquote form |
,@form |
#unquote-splicing form |
,@.items
#unquote-splicing #local items
#err <outside quasiquote splicing unquote>
`~(f ,.x ,@.xs)
#quote #quasiquote #invoke [f #unquote #local x #unquote-splicing #local xs]
~(f ,.x ,@.xs)
Quoting a string does not turn it into a DUST quotation form.
#unquote .name
,.name
#unquote #local name
#err <outside quasiquote unquote>
Quotation names have no special meaning inside parentheses: (quote 1)
parses as #invoke [quote 1] and formats as (quote 1).
The explicit quotation notation is #quote 1, whose canonical DUST spelling
is `1. The same rules apply to the other three quotation tags.
Quote returns its enclosed form as data without evaluating references, invocations, or collection contents within it. Quasiquote constructs data in the same way, except that unquote evaluates one enclosed form and substitutes its resulting value. Unquote-splicing evaluates an enclosed list and inserts its elements into the surrounding list, in order.
With x bound to 2 and xs bound to [3 4]:
`[a .x] ; returns [a .x]
~[a ,.x ,@.xs] ; returns [a 2 3 4]
Nested quasiquotes match unquotes by nesting depth: entering a quasiquote
increases the depth, and an unquote or splice belongs to its matching
quasiquote. An inner quasiquote's substitutions are not executed by an outer one.
Unquote outside quasiquote produces #err <outside quasiquote unquote>,
and unquote-splicing outside quasiquote produces
#err <outside quasiquote splicing unquote>.
Unquote-splicing outside a list-element position, or splicing a value that is
not a list, produces #err <invalid quotation>.
The DUST parser constructs the tagged elements. DUST decides how to resolve a reference, invoke a function, or process a quotation form. Those evaluation rules belong to DUST rather than the data format.
1.2.9. Application value prefixes ↑
The meanings of #link, #dur, #utc, and #hash belong to DUST. They use RON’s generic #prefix value notation; RON accepts these prefixes without interpreting them. Payload validation and evaluation semantics belong to DUST. Parsing preserves the tagged representation without evaluating it, except that a valid #utc or #dur payload is rewritten to its canonical spelling: #utc 1970-01-01T00:00Z reads and formats as #utc 1970-01-01T00:00:00Z, and #dur PT90M as #dur PT1H30M. Payloads that do not parse are kept for evaluation to reject.
Epsilon imports #utc as a typed UTC value. Evaluating it returns that same
value; it does not execute or reinterpret its timestamp payload. Formatting
exports the normal tagged representation through the selected notation codec.
#utc and #dur are distinct signed 64-bit counts of 100 ns ticks. UTC counts
from the Unix epoch; durations count elapsed ticks. Canonical text has at most
seven fractional second digits. Input accepts up to nine digits, rounded to the
nearest tick, with halfway cases rounded away from zero. A nonzero timestamp or
duration that would round to zero is represented by one tick in magnitude.
See the Time library for ranges, constructors, units,
calendar operations, and explicit tick/nanosecond conversion.
#link constructs a distinct link value with an integer, string, or tagged
payload. Integer payloads use the full signed 64-bit range. String payloads stay
strings, including numeric-looking names such as #link "42". Tagged payloads
retain their tag and value, as in #link #url https://arst.com/arst.
Bare strings are valid: #link foo carries the name foo. Epsilon preserves
names without resolving them; a query or transaction engine can interpret a name
as a temporary ID. Epsilon does not fetch or interpret tagged link targets.
A reference or invocation in the payload is evaluated before constructing the
link. For example, (let [foo 42] #link .foo) produces #link 42.
Quotation preserves constructor syntax: `#link .foo does not look up foo.
Booleans, floats, plain maps, and lists are invalid evaluated link payloads.
A payload that evaluates to an error is the result instead of a link:
#link .missing returns the unbound-name error, so error? and falsiness see it.
#link .missing #link (/ 1 0) (error? #link .missing)
#err <name unbound> #err <division integer zero> true
The canonical form #link 42 has this JSON representation:
{"#link": 42}
A link is distinct from an integer even when their payloads match.
Both (int? #link 42) and (number? #link 42) return false, and numeric
operations reject link operands. Integer link payloads sort before string payloads, then tagged payloads.
Payloads use their native ordering; tagged ordering requires scalar tag payloads.
Comparing #link 42 with the integer 42 produces a type-mismatch error.
(do [#link 0 #link -1 #link -9223372036854775808 #link 9223372036854775807])
#link 0 #link -1 #link -9223372036854775808 #link 9223372036854775807
(do [(= #link 42 #link 42) (= #link 42 #link 43) (comparison/identical? #link 42 #link 42)])
true false true
{#utc 2026-10-24T12:34:56Z}
#utc 2026-10-24T12:34:56Z
#utc 2026-10-24T12:34:56Z
{#utc 2026-10-24T12:34:56Z}
#utc 2026-10-24T12:34:56Z
#utc 2026-10-24T12:34:56Z
#utc 2026-10-24T12:34:56Z
{ dependsOn {#link 42} when {#utc 2026-09-09T00:00:00Z} }
{dependsOn #link 42 when #utc 2026-09-09T00:00:00Z}
dependsOn #link 42 when #utc 2026-09-09T00:00:00Z
{dependsOn #link 42 when #utc 2026-09-09T00:00:00Z}
#hash holds a digest tagged with its algorithm; the
hash library defines its payload and equality.
#hash #sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
#hash #sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
#hash #sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
1.2.10. Mute ↑
A backslash prefix, \form, mutes the immediately following complete form.
The prefix and its form are adjacent.
It applies to invocations, references, scalar values, lists, maps, and tagged elements.
The parser still reads the muted form's notation and balances its delimiters;
malformed strings or unmatched delimiters remain syntax errors.
A muted form is represented as a #mute tagged element whose payload is the
preserved form. For example, (+ 1 \(* 20 30) 2) has the form representation
#invoke [+ 1 #mute #invoke [* 20 30] 2].
The explicit #mute form spelling is accepted, and canonical DUST writes the
backslash shorthand. RON and JSON keep the tagged element.
(+ 1 #mute (* 20 30) 2)
(+ 1 \(* 20 30) 2)
#invoke [+ 1 #mute #invoke [* 20 30] 2]
When reading files or interpreting source lists and invocation argument sequences,
DUST excludes muted elements before evaluation, macro expansion, and arity or binding checks.
Their payloads are not evaluated or checked for semantic validity.
Muted elements contribute no element or argument, not a null placeholder.
The active forms must satisfy the usual rules.
Known tags inside a muted payload are still validated on import, so \#local 42
is invalid just as #local 42 is.
(+ 1 \(* 2 3) 4) ; evaluates to 5
[\.missing \[1 2] \{a 1}]
\.missing \[1 2] \{a 1}
[#mute #local missing #mute [1 2] #mute {a 1}]
A file containing only muted forms produces no evaluation results. A muted value
cannot be compiled directly as a root expression or supply a map value or invocation callee.
These uses are evaluation errors, not parse errors: #err <mute root value>,
#err <map mute value>, and #err <callee mute>.
Runtime data is not filtered for mute elements.
To begin a literal string with a backslash, quote the string.
["\\path" "\\(+ 1 2)"]
"\\path" "\\(+ 1 2)"
[ \path \(+ 1 2)]
"\\path" "\\(+ 1 2)"
1.2.11. Canonical expression formatting ↑
Canonical DUST formatting preserves the parsed form.
Consecutive forms in an invocation or packed list are separated by a space when they share a line.
Consecutive closing delimiters can be adjacent.
An invocation stays on one line when it fits RON's 100-column target.
Otherwise, it wraps between complete arguments, with continuation lines
indented two spaces from the invocation's starting column. This rule applies
to all callees without recognizing function or special-form names.
Valid references use .name and :name, invocations use parentheses (including ()),
quotation forms use their short prefixes, and muted forms use \. Explicit tagged data that has no
faithful shorthand, such as #local "two words", #const "", or #local ".x",
retains explicit notation. A direct callee whose name begins with . or :
also keeps the explicit #invoke notation. Formatting and parsing the result preserves the JSON representation.
Comments on invocation elements remain attached to those elements.
[ (quote 1) (quasiquote [a b]) (unquote .x) (unquote-splicing .xs) ]
(quote 1) (quasiquote [a b]) (unquote .x) (unquote-splicing .xs)
[#invoke [quote 1] #invoke [quasiquote [a b]] #invoke [unquote #local x] #invoke [unquote-splicing #local xs]]
{empty #local "" spaces #local "two words" non-string #local 42 quoted-name #local ".x" colon-name #const ":x" non-list #invoke 42 numeric-callee #invoke [1 2] spaced-callee #invoke ["two words" 1] var-callee #invoke [#local f 1] const-callee #invoke [#const f 1] dotted-callee #invoke [".f" 1] quoted-callee #invoke [#quote .f 1] empty-call #invoke []}
{colon-name #const ":x" const-callee (:f 1) dotted-callee #invoke [".f" 1] empty #local "" empty-call () non-list #invoke 42 non-string #local 42 numeric-callee #invoke [1 2] quoted-callee (`.f 1) quoted-name #local ".x" spaced-callee #invoke [ two words 1] spaces #local "two words" var-callee (.f 1)}
colon-name #const :x const-callee #invoke [#const f 1] dotted-callee #invoke [.f 1] empty #local "" empty-call #invoke [] non-list #invoke 42 non-string #local 42 numeric-callee #invoke [1 2] quoted-callee #invoke [#quote #local f 1] quoted-name #local .x spaced-callee #invoke [ two words 1] spaces #local "two words" var-callee #invoke [#local f 1]
(function-name "a string argument with several words" "a string argument with several words" "a string argument with several words" "a string argument with several words" )
(function-name
"a string argument with several words"
"a string argument with several words"
"a string argument with several words"
"a string argument with several words")
#invoke [
function-name
a string argument with several words
a string argument with several words
a string argument with several words
a string argument with several words]
. or :.
["foo(bar)" "(bar)" "abc)" ".name" ":name"]
"foo(bar)" "(bar)" "abc)" ".name" ":name"
[foo(bar) (bar) abc) .name :name]
These rules apply to DUST source, not str, which formats collections through the canonical RON formatter.
1.3. Values and Evaluation ↑
DUST is an expression-oriented programming language, thus every language construct evaluates to a value. We call these language constructs forms. Forms beget values by evaluation. At the bottom lie the self-evaluating values. These are the values that evaluate to themselves.
null is the value that represents the absence of a value.
null is falsey.
true is the value that represents truth.
true is truthy.
false is the value that represents falsehood.
false is falsey.
Strings are RON strings. Strings are interned.
Numbers are RON numbers: integers are signed 64-bit and floats are IEEE 754
doubles. A literal outside those ranges, such as 1e400, is a parse error.
Numbers never convert implicitly. Arithmetic and comparison operands must be
all integers or all floats, so (+ 1 2.5) is #err <mismatch type>; write
(+ 1.0 2.5) or convert with float and
int. Division is the one operation whose result kind
differs from its operands: / always returns a float.
Operations check their results. Integer overflow is #err <integer overflow>,
a float result that would be infinite or NaN is #err <float overflow>, and
division by zero is #err <division integer zero> or
#err <division float zero> by operand kind. NaN and infinity therefore never
exist as numbers: a value can only hold a number that JSON can spell.
DUST does not have keywords because RON does not have keywords. But importantly: keywords are not necessary in a language like RON and DUST where strings are interned and supported first-class (no quotes)
1.3.1. References ↑
However DUST does need references. A reference is a name that refers to something else; in other languages you might know it as an identifier. DUST has two kinds, and the prefix says where the name is looked up:
| Source | Tagged form | JSON | Looks up |
|---|---|---|---|
.name |
#local name |
{"#local": "name"} |
Local bindings: let, fn parameters, a function's own name, for, and top-level local |
:name |
#const name |
{"#const": "name"} |
Constants: library entries and bindings supplied by the host |
References follow the source notation rules.
A quoted string whose contents begin with . or : is not a reference.
Map keys are always strings, including names beginning with . or :.
Parsing constructs a reference without resolving its name.
Binding forms use string names rather than references.
A local reference resolves to the nearest enclosing local binding, whether
its value is a function or ordinary data. With no enclosing binding, it is
the top-level local of that name, read each time the reference runs. It
never reaches a constant: .math/pi is unbound unless a local binding has
that name. An unbound local is an error when evaluation reaches it, so
(if true 1 .missing) returns 1.
A constant reference never reaches a local binding: (let [x 1] :x) is
unbound. Library entries are fixed: a host binding with the same name as a
library entry is rejected, and every evaluation with that binding produces
#err <conflict name>, whether or not the program refers to the name. A host
that needs its own value supplies it under another namespace.
Every constant in a program is resolved once, before evaluation starts. If any
of them is unbound, the program evaluates to that #err <name unbound>, even
when the reference is in a branch that would not run. Constants inside quoted
or muted forms are not evaluated, so they are not resolved.
A pointer has the type name pointer and holds a sequence of Key string
segments and nonnegative Index integer segments. RFC 6901 text is used when
constructing, encoding, or printing it. Decoding splits at /, replaces ~1
with / and ~0 with ~, and treats decimal digits with no leading zeroes
as an index, saturating at the largest signed 64-bit integer. Encoding writes
indices in decimal and escapes ~ before / in keys. After printing and
reading again, Key "0" becomes Index 0. The two select the same map key,
but only the index can select a list element.
Pointer equality compares segment kinds and values. list? is false for a
pointer, and jsonptr? is true. str returns the RFC 6901 text, while format
and the REPL print the canonical DUST form. Functions that accept arbitrary
values also accept pointers, including equality, distinctness, and map values.
A direct invocation resolves the exact name stored in the first element of #invoke.
It looks first in the local bindings and then in the constants, so both
(increment 4) with a let-bound increment and (+ 1 2) work.
("+" 1 2) looks up a name containing the quote characters as well as the plus sign.
(.f 1) and (:+ 1 2) are computed callees whose references follow the rules above.
A missing binding produces an unbound-name error.
1.3.2. Evaluation ↑
Evaluation turns a form into a value. RON and JSON representations of the same source form have identical evaluation semantics. Runtime inputs and returned values are values, not source to evaluate again.
Known tagged elements are validated before evaluation. This includes known tags
inside quotations, muted forms, and branches that would not be selected at runtime.
Unknown tags remain generic tagged data. Evaluating one returns its tag and
payload as data, without evaluating the payload or any nested invocation.
For example, #custom (/ 1 0) preserves the invocation as data and does not
divide by zero. Formatting and notation round trips preserve the normal tagged
representation.
This opaque evaluation rule does not bypass import validation. Known tags
nested inside an unknown payload are still validated eagerly; for example,
#custom #local 42 has an invalid known reference payload. Today that is the
error #err <invalid tag> when the form is evaluated; once readers use the
cell decoders, such a source does not parse.
#application { symbol .missing invocation (unknown-function .missing) unquote ,.missing splice ,@.missing nested #another (unknown-function (/ 1 0)) }
#application {invocation (unknown-function .missing) nested #another (unknown-function (/ 1 0)) splice ,@.missing symbol .missing unquote ,.missing}
#application {invocation (unknown-function .missing) nested #another (unknown-function (/ 1 0)) splice ,@.missing symbol .missing unquote ,.missing}
The self-evaluating forms already represent their value, so evaluating them results in themselves. Other forms describe how to obtain a value, for example by looking up a name to grab what it represents or invoking a function.
An invocation is encoded as a #invoke tagged element containing the callee followed by its argument forms.
For an ordinary function call, a string in the first element of #invoke names the function to resolve.
Any other form in that position is evaluated once to obtain the function.
Direct invocation names resolve to the nearest local binding, then to a
constant. References resolve as described in References.
The result must be a function.
A computed callee that returns a string is an error, even if that string names a function. There is no second name lookup.
DUST excludes muted argument forms, then evaluates the active argument forms from left to right and passes their resulting values to the function. The function's return value becomes the value of the invocation.
Examples show DUST source beside its expected result.
(+ 1 (* 2 2)) can be thought of as being evaluated in two steps:
- Evaluate
(* 2 2)to obtain4. - Substitute
4for the multiplication, giving(+ 1 4), which evaluates to5.
Hence:
Square brackets construct lists and braces construct maps. List construction excludes muted elements and evaluates the active elements from left to right. Map construction evaluates each member value2; the order in which member values are evaluated is not defined. Canonical key order is a formatting rule, not an evaluation order. A muted form cannot supply a map member value.
Tags parse into ordinary boolean map members before evaluation. Their names are string keys, not references, and their boolean values evaluate to themselves. Tags introduce no new evaluation behavior.
An evaluation error is a value. It can be passed to a function, bound by let,
or stored in a list or map. Evaluation of other arguments and elements can
continue. A program can inspect an error with error?.
An error is a #err tagged element. Its payload may be a string or a detailed
map. A map can contain a RON tag group that classifies the error and members
that describe the failure:
#err {<division integer zero>
column 1
line 1
message "division by zero"}
Conformance expectations write only the tags, as #err <division integer zero>;
messages and positions are diagnostics, not part of the contract.
An error value retains its tags when passed through a program. Resource and
allocation failures remain runtime failures that can stop evaluation.
Special forms control evaluation rather than following the ordinary function-call rule.
For example, if evaluates only the selected branch.
Macros transform forms before evaluation, while quotation allows a form to be used as data without evaluating it.
1.3.2.1. Error tags ↑
Tags are a set, not a path: each tag is one word naming one facet of the
failure, so an error can be looked up by any predicate it carries. overflow
matches both integer and float overflow, integer matches every failure of
integer arithmetic, mismatch matches type and arity mismatches, and mute
matches every misuse of a muted form. Language errors use the tags below; Stardust errors carry
stardust, and applications should add a tag naming themselves.
| Tags | Raised when | Example |
|---|---|---|
<mismatch type> |
An operand has the wrong type, including mixed integer and float arithmetic | (+ 1 2.5) |
<integer overflow> |
An integer, duration, or timestamp result leaves the 64-bit range | (+ 9223372036854775807 1) |
<float overflow> |
A float result would be infinite or NaN | (* 1e308 10.0) |
<division integer zero> |
An integer or duration divisor is zero | (/ 1 0) |
<division float zero> |
A float divisor is zero | (/ 1.0 0.0) |
<invalid value> |
An argument has the right type but an unusable value | (time/duration forever) |
<invalid tag> |
A known tag has a malformed payload | #utc yesterday |
<name unbound> |
A reference or callee has no visible binding | .missing |
<conflict name> |
A host binding has the name of a library entry | binding math/pi |
<arity mismatch> |
A function receives a number of arguments it does not accept | ((fn [x] x)) |
<argument missing> |
A variadic operator receives no arguments where one is required | (/) |
<list missing> |
A function that takes lists receives none | (map :strings/case) |
<expected function> |
A callee evaluates to a non-function | (map upper [ada]) |
<callee string> |
A computed callee evaluates to a string | ((str "a") 1) |
<fn missing name rest> |
& is not followed by a rest parameter name |
(fn [&] 1) |
<arity fn multiple variadic> |
More than one arity is variadic | (fn ([& a] 1) ([x & b] 2)) |
<arity duplicate fixed fn> |
Two arities take the same fixed count | (fn ([x] 1) ([y] 2)) |
<arity exceeds fixed fn variadic> |
A fixed arity exceeds the variadic arity's minimum | (fn ([x y] 1) ([& r] 2)) |
<mute root value> |
A muted form supplies the root value | \1 |
<map mute value> |
A muted form supplies a map member value | {a \1} |
<callee mute> |
A muted form supplies a callee | (\f 1) |
<outside quasiquote unquote> |
Unquote appears outside quasiquote | ,.x |
<outside quasiquote splicing unquote> |
Unquote-splicing appears outside quasiquote | ,@.x |
<invalid quotation> |
Quasiquote splices into something other than a list | #quasiquote {a #unquote-splicing [1]} |
<algorithm hash unknown> |
hash/digest receives an unsupported algorithm |
(hash/digest md5 1) |
<function unrepresentable> |
A host asks for a function value without its source form | |
<invalid program> |
The engine rejects a program before evaluation | |
<configuration invalid> |
The host configured the engine incorrectly | |
<limit resource> |
Parsing or evaluation exceeds a CPU, memory, node, or depth limit | 257 nested lists |
<limit internal KIND> |
A valid program is outside an internal limit of the implementation | 256 nested lists in Epsilon |
<allocation failure> |
The host cannot allocate memory | |
<failure runtime> |
An internal engine failure |
1.3.3. Collection Paths ↑
Collection paths select values in maps and lists. A string is always one
literal map key, including a string that starts with /. A nonnegative
integer is one index. A list contains only string keys and nonnegative integer
indices; an empty list selects the root. A pointer from /..., #jsonptr,
or jsonptr supplies its segments directly.
| Segment | Map | List | Null or missing entry |
|---|---|---|---|
Key s |
Member s |
#err <mismatch type> |
Missing |
Index n |
Member with decimal key n |
Zero-based element n |
Missing |
An out-of-range list index is missing. A missing read returns the default or
null, and contains? returns false. A write creates intermediate maps;
an index in a new map uses its decimal key. A list write at its length appends.
Other container types give #err <mismatch type>. A negative index gives
#err <invalid value>. A pointer inside a list path is a type error.
A whole embedded segment becomes one segment based on its value:
| Embedded value | Result |
|---|---|
| String with valid index spelling | Index |
| Other string | Key, without further RFC 6901 decoding |
| Nonnegative integer | Index |
| Negative integer | #err <invalid value> |
| Null, boolean, float, list, map, pointer, or function | #err <mismatch type> |
| Error | The same error |
A mixed segment always becomes a key, even if its resulting text is digits.
Its literal parts and embedded strings are concatenated directly; null becomes
null, and other values use their canonical DUST text.
1.4. Scope & Binding ↑
DUST is a lexically scoped language. What one might call variables in another language are called bindings in DUST. A scope is the region of code where a binding is valid. A reference with no visible binding produces an unbound-name error.
Libraries are loaded under a namespace, and a library file names the members
it provides with export. What a library path resolves to is
defined by each runtime: the Epsilon REPL resolves !load lib/math to the file
lib/math.dust, relative to its working directory, while Stardust resolves
paths within its database.
To create local bindings you need to introduce a new scope, and in DUST, like many lisps, the main way to do that is with the let special form.
1.5. Identity & Equality ↑
Equality asks whether two values contain the same data, regardless of how they were constructed or where they came from. Whereas identity asks whether references point to the same value.
Equality in DUST is by value, except for functions which compare by identity.
Equal values have equal hashes, because a hash covers a value's canonical bytes, which keep the integer/float distinction and drop comments. The converse fails only for functions: two closures with the same source hash alike but are not equal, since each is its own identity.
Since functions, constructs that do not exist in JSON or RON, can close over state it only makes sense for them compare by identity.
DUST provides = to test equality and comparison/identical? to test identity.
1.6. Truth and Control Flow ↑
Control-flow constructs in DUST are also expressions (reminder: everything is an expression), so they evaluate to values. DUST provides the following special forms and macros to enable branching and repetition. They decide which forms get evaluated and how often.
Every condition must be interpreted as either logically true or false. We must categorize each value as either logically true or false. DUST defines truth and falsehood so:
- `null`, `false`, and
#errvalues are falsey - everything else is truthy
An error used as a test therefore selects the false branch: (if ( 1 null) a b)= is b, since ( 1 null)= is an #err value.
We say that they are false/y/ or truth/y/ because they are logically true or false. To say they are true or false would imply they equal true or false, which obviously makes no sense.
DUST provides true? and false? to answer if some value is true or false. And boolean to coerce a value to true or false, and not to negate a value's truthiness.
1.7. Special forms and macros ↑
Special forms and macros control evaluation or introduce bindings. Ordinary functions are documented in the Function Index.
1.7.1. Function construction ↑
(fn name? doc-or-meta? [params*] body*) → function
(fn name? doc-or-meta? ([params*] body*)+) → function
Compiles a function literal into a closure.
The optional name is local to the function and supports self-reference from every arity body.
Functions can be selected, bound, passed, returned, and called.
A function's canonical form is its fn source, so printing and
hashing a closure use that form; values it captures are not part of
it. A library function's canonical form is its constant reference, such as :strings/case.
Recursion is supported.
The optional function name and parameter names are strings, conventionally written bare.
References to parameters in the body use ..
The optional doc-or-meta slot accepts either a string docstring or a map of metadata.
A string docstring supplies the dust/doc entry in the function's metadata.
If the first argument after fn is a string, it is always the function name, even when written with quotes.
A string docstring can appear only after a name.
An anonymous function can supply documentation in a metadata map before its parameter list or arity clauses.
Leading comments before the parameter list or first arity clause become the binary
function definition's doc field. A comment on the fn constructor joins that
block. Canonical formatting puts the block before the parameters or first clause;
comments inside parameters and bodies stay on their values.
See the constructor example and its
canonical form.
1.7.1.1.1. Parameters and bodies ↑
A parameter list without & accepts exactly as many arguments as it has parameters.
An empty parameter list accepts no arguments.
A parameter list ending in & rest accepts additional arguments after its required parameters.
The rest parameter receives those additional values as a nonempty list, in order, or null when there are none.
The values are passed without copying or evaluating them again.
The & marker can occur once at most and must be followed by exactly one parameter name.
It is recognized by its string value, so "&" has the same meaning.
A malformed parameter list is an error when compiling the function.
A required parameter may also be a map pattern. Each pattern key selects the
same key from the argument map; its value names the local binding. A nested
map pattern destructures that selected value recursively. Missing keys and
null arguments supply null to the pattern. A non-map, non-null argument
is a type error. Rest parameters remain string names. Other parameter pattern
types are compilation errors. This map destructuring applies to fn
parameters; it does not add destructuring to let or for.
For example, (fn [{quantity quantity unitPrice unitPrice}] (* .quantity .unitPrice))
binds the quantity and unitPrice members of its argument.
A body contains zero or more expressions, evaluated in order.
Its final expression supplies the return value.
An empty body returns null.
An error value does not by itself stop later body expressions.
1.7.1.1.2. Multiple arities ↑
The single-arity form has one parameter list followed by its body expressions.
The clause form has one or more parenthesized arity clauses, each with a parameter list followed by its body expressions.
Both forms create one function.
The optional name and documentation or metadata apply to the whole function.
An arity clause parses as a #invoke tagged element containing its parameter list followed by its body forms.
fn interprets this structure as a clause, not a call.
A function can have distinct fixed arities and at most one variadic clause. With a variadic clause, no fixed arity may exceed its required parameter count. Duplicate fixed arities, multiple variadic clauses, and fixed arities above that count are errors when compiling the function.
A call selects an exact fixed-arity match first.
Otherwise, it selects the variadic clause if enough arguments were supplied for its required parameters.
If neither matches, the call produces an arity error.
Selection depends on argument count, not argument types or clause order.
Only the selected body is evaluated, with parameter bindings local to that call.
Ordinary calls and apply use the same selection rules.
Functions capture their lexical bindings, including bindings retained by escaping closures. Top-level functions can call each other through top-level locals. Tail-call guarantees remain to be defined.
1.7.1.1.3. Examples ↑
(let [increment (fn increment [x] (+ 1 .x))] [(increment 0) (increment 1) (increment 41)])
(let [increment (fn increment [x] (+ 1 .x))] [(increment 0) (increment 1) (increment 41)])
1 2 42
(let [increment (fn increment "Return x plus one." [x] (+ 1 .x))] [(increment 0) (increment 1) (increment 41)])
(let [increment (fn increment "Return x plus one." [x] (+ 1 .x))] [(increment 0) (increment 1) (increment 41)])
1 2 42
(let [increment (fn increment {dust/doc "Return x plus one."} [x] (+ 1 .x))] [(increment 0) (increment 1) (increment 41)])
(let [increment (fn increment {dust/doc "Return x plus one."} [x] (+ 1 .x))] [(increment 0) (increment 1) (increment 41)])
1 2 42
(let [increment (fn {dust/doc "Return x plus one."} [x] (+ 1 .x))] [(increment 0) (increment 1) (increment 41)])
(let [increment (fn {dust/doc "Return x plus one."} [x] (+ 1 .x))] [(increment 0) (increment 1) (increment 41)])
1 2 42
(let [increment (fn [x] (+ 1 .x))] (increment 4))
(let [increment (fn [x] (+ 1 .x))] (increment 4))
(let [greet (fn greet "Greet one or more people." ([name greeting & more] (strings/join [.greeting ", " .name (strings/join .more "")] "")) ([] (greet world)) ([name] (greet .name Hello)) ([name greeting] (strings/join [.greeting ", " .name "!"] "")))] [(greet) (greet Ada) (greet Ada Hi) (greet Ada Hi " and " Bob "!")])
(let [greet (fn greet "Greet one or more people." ([name greeting & more] (strings/join [ .greeting ", " .name (strings/join .more "")] "")) ([] (greet world)) ([name] (greet .name Hello)) ([name greeting] (strings/join [ .greeting ", " .name !] "")))] [(greet) (greet Ada) (greet Ada Hi) (greet Ada Hi " and " Bob !)])
"Hello, world!" "Hello, Ada!" "Hi, Ada!" "Hi, Ada and Bob!"
(do expr*) → value
Evaluates the expr forms in order and returns the last result.
With no expressions, returns null.
An error value does not by itself stop later expressions.
do introduces no bindings.
[(do) (do 7) (do 1 2 3) (if true (do ignored 42) (/ 1 0))]
(do) (do 7) (do 1 2 3) (if true (do ignored 42) (/ 1 0))
null 7 3 42
(let [bindings*] exprs*)
Introduces local bindings and evaluates a body in their scope.
The binding list contains zero or more pairs of names and value expressions.
Names are strings, conventionally written bare.
A reference such as .price is not a binding name.
(let [price (* 5 2) quantity 3] (* .price .quantity))
(let [price (* 5 2) quantity 3] (* .price .quantity))
The value expressions are evaluated once and in order. Each binding becomes available to use in later expressions.
(let [price 10 quantity 3 total (* .price .quantity)] .total)
(let [price 10 quantity 3 total (* .price .quantity)] .total)
Bindings are in scope throughout the body of their let and within nested expressions.
Inner scopes can introduce the same names, shadowing the outer bindings.
(let [price 10] [(let [price (+ .price 2)] .price) .price])
(let [price 10] [(let [price (+ .price 2)] .price) .price])
12 10
A function closes over the bindings it references from its surrounding lexical scope. The resulting closure has those bindings available when the function is called, even after execution has left that scope (indefinite extent).
(let [name outer read-name (fn [] .name)] (let [name inner] (read-name)))
(let [name outer read-name (fn [] .name)] (let [name inner] (read-name)))
outer
(let [read-name (let [name outer] (fn [] .name))] (read-name))
(let [read-name (let [name outer] (fn [] .name))] (read-name))
outer
Body expressions inside the let are evaluated in order.
The let special form is an expression and evaluates to the value of the final expression in the body of the let, unless the body is empty in which case it evaluates to null.
Additional forms that introduce bindings are: fn, for, and local.
(local name value?) → local reference
Defines a top-level local: a binding that the later top-level forms of the
same file, or of the same REPL session, read as .name.
local is a top-level form. Inside any other form it is
#err <invalid program>. Each top-level form is evaluated on its own, so a
form sees the locals that earlier forms define. (local name value) returns
the reference .name.
A later local with the same name gives the same binding a new value. A
reference to a top-level local is read each time it runs, so a function made
earlier sees the new value, and a function can call a function that is
defined after it, once that function exists.
(local name) declares a local without a value, and leaves the value of a
local that has one. A declared local is #err <name unbound> until a later
local gives it a value. A declaration lets two top-level functions call
each other, as in Clojure's declare.
(local odd?) (local even? (fn [n] (if (= .n 0) true (odd? (- .n 1))))) (local odd? (fn [n] (if (= .n 0) false (even? (- .n 1))))) (even? 10)
.odd? .even? .odd? true
When the value is an #err value, local returns that value and the local
is declared without a value.
A let, fn, or for binding with the same name shadows a top-level local.
A constant reference never reaches a top-level local. The name of a library
entry is #err <conflict name>, because a direct call of that name always
calls the library entry.
(export name*) → list
Names the top-level locals that a library file provides. When a runtime
loads the file under a namespace, as !load lib/shapes s does in the
Epsilon REPL, each named local becomes the constant :s/name, with the
value the local has when the file has run. The other locals of the file
stay private, and the file's functions read them, not the locals of the
code that calls them.
; lib/shapes.dust
(local area (fn [w h] (* .w .h)))
(local square (fn [s] (area .s .s)))
(local unit 1)
(export square unit)
After !load lib/shapes s, (s/square 3) returns 9 and :s/unit returns
1, while :s/area is #err <name unbound>.
export is a top-level form, and a file can have more than one. A file
with no export provides nothing. A load fails when export names a local
that does not exist or has no value, or when a top-level form returns an
#err value. Evaluated directly, export returns the list of the named
references and has no other effect.
1.7.5. Control Flow ↑
(and forms*) → value
Evaluates forms once each, from left to right, and returns the first falsey
value without evaluating later forms; an #err value is falsey, so and returns it. If every form is truthy, returns the
last value. With no forms, returns true.
Only false, null, and #err values are falsey; zero and the empty string are truthy.
The result is an operand value rather than a conversion to boolean.
(and) (and 0 "" x) (and false (/ 1 0)) (and null (/ 1 0)) (let [m {n 0}] [(and (do (patch .m {n (+ (get .m n) 1)}) x) y) (get .m n)])
true x false null [y 1]
(or forms*) → value
Evaluates forms once each, from left to right, and returns the first truthy
value without evaluating later forms. If every form is falsey, returns the
last value. With no forms, returns null.
Only false, null, and #err values are falsey; zero and the empty string are truthy.
The result is an operand value rather than a conversion to boolean.
(or) (or null false x) (or 0 (/ 1 0)) (or "" (/ 1 0)) (let [m {n 0}] [(or (do (patch .m {n (+ (get .m n) 1)}) null) y) (get .m n)])
null x 0 "" [y 1]
(if test then)
(if test then else)
Selects a branch according to the truthiness of a test.
The test is evaluated once. The resulting value's truthiness is used to choose a branch.
A truthy result selects then, and a falsey result selects else.
Only the selected branch is evaluated, and its value becomes the value of the whole if expression.
If else is omitted, a falsey test evaluates to null.
Each branch contains one expression.
if is a special form because it controls which arguments are evaluated, rather than evaluating all of them before making the choice.
(when test body...)
Runs a body when its test is truthy.
It evaluates the test once, then evaluates the body expressions in order and returns the last result.
If the test is falsey, the body is not evaluated and the result is null.
An empty body also returns null.
Unlike if, when has no alternative branch and can contain several body expressions.
(when-not test body...)
Runs a body when its test is falsey.
Like when, it evaluates the test once, evaluates the selected body in order, and returns the last result.
A truthy test skips the body and returns null, and an empty body returns null regardless of the test's value.
(cond test result ...)
Chooses among pairs of tests and result expressions.
It evaluates tests once each, in order, until one is truthy.
It then evaluates and returns that test's result expression, without evaluating any later tests or results.
A selected result of false or null is still the result, not a reason to continue to another pair.
If no test is truthy, cond returns null.
With no pairs, it also returns null.
Every test must have a result expression, so an unmatched final test is an error during macro expansion.
A final pair with true as its test supplies a default result.
This is an ordinary truthy test, not a separate default-branch syntax.
(for [name collection ...] body)
Evaluates a body for each combination of collection values and returns a list of the results.
Evaluation is eager: all iterations complete before for returns.
Binding names are strings, conventionally written bare, rather than references.
The bindings are local to the for.
Multiple binding pairs introduce nested iteration, with the leftmost binding as the outermost loop. Lists are traversed in order. The body evaluates once per combination, contributing one value to the result list.
(for [x [1 2] y [10 20]] (+ .x .y))
(for [x [1 2] y [10 20]] (+ .x .y))
11 21 12 22
Each collection expression is evaluated once each time its loop is entered. Later collection expressions can reference earlier bindings. An empty collection contributes no combinations.
(for [xs [[1 2] [3]] x .xs] .x)
(for [xs [[1 2] [3]] x .xs] .x)
1 2 3
The body contains one expression.
A let in the body can introduce additional bindings for calculated values.
If the body produces a list, that list is one element of the result.
1.7.6. Quotation ↑
#quote form
Returns the enclosed form as data without evaluating it. References, invocations, and collection contents retain their form representation. See Quotation forms for prefix syntax and encoding.
1.7.6.2. quasiquote ↑
Macro#quasiquote form
Constructs data while evaluating unquotes at the matching nesting depth. List-element splices insert the elements of their evaluated list. See Quotation forms for prefix syntax and encoding.
#unquote form
Evaluates the enclosed form within its matching quasiquote and substitutes the resulting value. Outside quasiquote, this is an error. See Quotation forms for prefix syntax and encoding.
1.7.6.4. unquote-splicing ↑
Macro#unquote-splicing form
Evaluates the enclosed list within its matching quasiquote and inserts its elements into the surrounding list. A non-list result or a splice outside a list-element position is an error. See Quotation forms for prefix syntax and encoding.
2. Core Functions ↑
The core library holds every function whose name has no prefix, such as map,
+, and str. Prefixed names belong to other libraries, such as
time/now. See DUST for source notation, evaluation, and
special forms, and the Function Index for all callable entries.
Ordinary function arguments evaluate before the call. Functions can be passed
as values and called through apply. This page covers function calls,
equality, and booleans; the rest of the core library is grouped by area:
- Collections — Lists and maps: access, update, transformation, and generation.
- Order — Comparison, sorting, uniqueness, and grouping.
- Types — Type predicates, conversion, and formatting.
- Arithmetic — Operators and bounds.
- Collection library — Namespaced list, map, and path operations.
- Math library — Namespaced numeric operations.
- Hash library — Digests of DUST values.
- Random library — Seeded random generators.
(apply function arguments) → value
Calls a function value with the elements of a list as its arguments.
Both operands are evaluated before the call.
The first must produce an ordinary function, and the second must produce a list or null.
A null argument list supplies no arguments.
A string naming a function is not a function value.
Special forms and macros cannot be called through apply.
The list elements are passed as values, without evaluating them again as source.
(let [increment (fn [x] (+ 1 .x))] (apply .increment [4]))
(let [increment (fn [x] (+ 1 .x))] (apply .increment [4]))
(= x) → boolean
(= x y) → boolean
(= x y more*) → boolean
Returns true if all supplied values are equal, otherwise false.
With one argument, returns true.
With no arguments, produces an arity error.
With two or more arguments, compares adjacent values from left to right.
The first unequal pair returns false without comparing later pairs.
If every pair is equal, returns true.
Each compared pair must have the same type.
A type mismatch in a pair being compared produces #err <mismatch type>.
Types are not checked in advance across all arguments.
For example, ( 1 2 "2")= returns false, but ( 1 1 "1")= produces a type error.
Integers and floats are different types, so ( 1 1.0)= produces a type error.
= is an ordinary function.
Its argument expressions are evaluated before comparison begins.
Short-circuiting skips comparisons, not argument evaluation.
Thus ( 1 2 (/ 1 0))= produces a division-by-zero error.
- Numbers of the same type compare by value.
- Links compare their signed 64-bit payloads; a link and an integer have different types.
- Lists compare positionally.
- Maps compare keys and values independently of member order.
- Functions compare by identity.
[(= 1) (= null) (= [1 2]) (= (fn [])) (= 1 1) (= 1 2) (= 1 1 1) (= 1 1 2) (= 1 2 1) (= 1 2 "2") (= 1 1 2 null) (= null null null) (= [1] [1] [1])]
(= 1) (= null) (= [1 2]) (= (fn [])) (= 1 1) (= 1 2) (= 1 1 1) (= 1 1 2) (= 1 2 1) (= 1 2 "2") (= 1 1 2 null) (= null null null) (= [1] [1] [1])
true true true true true false true false false false false true true
(let [a {x 1} b {x 1} c .a] (= .a .b))
(let [a {x 1} b {x 1} c .a] (= .a .b))
true
(let [a {x 1} b {x 1} c .a] (= .a .c))
(let [a {x 1} b {x 1} c .a] (= .a .c))
true
(let [f (fn [x] .x) g .f h (fn [x] .x)] (= .f .g))
(let [f (fn [x] .x) g .f h (fn [x] .x)] (= .f .g))
true
(let [f (fn [x] .x) g .f h (fn [x] .x)] (= .f .h))
(let [f (fn [x] .x) g .f h (fn [x] .x)] (= .f .h))
false
(let [f (fn [x] .x) g .f h (fn [x] .x)] (= .f a))
(let [f (fn [x] .x) g .f h (fn [x] .x)] (= .f a))
#err <mismatch type>
(let [f (fn [x] .x) g .f h (fn [x] .x)] (= [.f] [.g]))
(let [f (fn [x] .x) g .f h (fn [x] .x)] (= [.f] [.g]))
true
(let [f (fn [x] .x) g .f h (fn [x] .x)] (= [.f] [.h]))
(let [f (fn [x] .x) g .f h (fn [x] .x)] (= [.f] [.h]))
false
(let [f (fn [x] .x) g .f h (fn [x] .x)] (= {callback .f} {callback .g}))
(let [f (fn [x] .x) g .f h (fn [x] .x)] (= {callback .f} {callback .g}))
true
(not value) → boolean
Returns true if value is null or false, otherwise false.
2.4. Collections ↑
These core functions build, read, and transform lists and maps. Functions that change a
collection change it in place and return it; collections/copy makes a copy to change
instead. They are part of the
core library; see DUST for source notation, evaluation, and
special forms.
2.4.1. Collection Functions ↑
Pointers can be used where these list functions accept a list. Pointer segments are strings for keys and integers for indices.
| Function | Pointer behavior |
|---|---|
count, empty? |
Count segments or test for no segments |
first, second, last, nth |
Return one segment value |
rest, slice, take, drop |
Return a pointer containing the selected segments |
concat |
Return a pointer when the first argument is a pointer; list-first concatenation returns a list |
map, filter, reduce, some, every?, take-while, drop-while |
Iterate segment values and return the usual list-function result |
Pointers are values that do not change, so the functions that change a list, such as
lists/append, lists/reverse, and sort, reject pointers with #err <mismatch type>.
Unless stated otherwise, list operations that make a new list accept null as an empty list, and map operations that make a new map accept null as an empty map.
Functions that change a collection require a target of the stated collection type, not null.
Map keys are strings.
Callbacks receive existing values without copying. Their side effects are not undone.
(map f xs) → list
(map f xs lists*) → list
Returns a list of the results of applying f to corresponding elements of xs and any additional lists.
The function receives one argument from each input list.
Calls occur in sequence order and stop when the shortest input is exhausted.
The result is built eagerly. Inputs are lists, pointers, or null, with
null treated as an empty list.
[(map (fn [x] (strings/case upper .x)) [ada "straße" ""]) (map :+ [1 2] [10 20 30]) (map :+ [1 2] [10] [100 200]) (map :+ [1 2] [10 20] [100] [1000 2000]) (map (fn [x] (strings/case upper .x)) []) (map (fn [x] (strings/case upper .x)) null) (map :+ [1] null) (map (fn [x] [.x]) [1 2])]
(map (fn [x] (strings/case upper .x)) [ada straße ""]) (map :+ [1 2] [10 20 30]) (map :+ [1 2] [10] [100 200]) (map :+ [1 2] [10 20] [100] [1000 2000]) (map (fn [x] (strings/case upper .x)) []) (map (fn [x] (strings/case upper .x)) null) (map :+ [1] null) (map (fn [x] [.x]) [1 2])
[[ADA STRASSE ""] [11 22] [111] [1111] [] [] [] [[1] [2]]]
(reduce function initial list) → value
Starts with initial and calls function with the accumulator and each list
element, in list order. Each call's result becomes the next accumulator.
Returns the final accumulator. An empty list or null returns initial
without calling the function. The first argument must be an ordinary function
value and the third must be a list, pointer, or null. Errors stop reduction immediately: a call
that returns an #err value ends the reduction, and that value is the result.
(reduce :+ 0 [1 2 3]) (reduce :- 10 [1 2 3]) (reduce (fn [acc x] (strings/join [.acc .x] "")) "" [a b c]) (reduce :+ 9007199254740993 [1 1])
6 4 abc 9007199254740995
(reduce (fn [acc x] (/ 1 0)) seed []) (reduce :+ false null) (reduce :+ null [])
seed false null
(patch map changes*) → map
Applies JSON Merge Patch member semantics to map in place and returns the same map.
All arguments must be maps, checked before any patch is applied.
With no changes maps, returns map unchanged.
Applies changes maps from left to right. Within each map, processes keys in ascending Unicode order:
nullremoves the member. A missing member is a no-op.- A map recursively patches the corresponding map. A missing or non-map destination is replaced with an empty map first.
- Any other value replaces the member without copying. Lists are replaced whole, not patched element by element.
Omitted members are unchanged. Nested patch maps are traversed, not installed by reference.
Collects the keys of each patch map before visiting its members, then reads each value when its key is processed, ignoring keys since removed.
Patch sources that alias the target observe earlier mutations.
Encountering a cycle through patch maps produces #err <invalid value> before applying the cyclic member. Earlier writes are not undone.
(patch {} a b) merges into a fresh map, and (patch (collections/copy m shallow) changes) into a copy of m.
Neither is a deep copy of the changes: null-valued members are omitted and list values are shared.
Use put to store null as a value.
(let [m {a 1}] [(patch .m {a 2}) .m]) (patch {} {a {b 1}} {a {c 2}}) (patch {a 1} {a null})
[[{a 2} {a 2}] {a {b 1 c 2}} {}]
(patch {items [a b c] keep 1} {items [x null] missing null}) (patch {a 1} {a {b 2 gone null}}) (patch {a {b 1 c 2}} {a {b null}})
{items [x null] keep 1} {a {b 2}} {a {c 2}}
(count collection) → integer
Returns the number of list elements, pointer segments, map members, or Unicode string code points.
For null, returns zero.
For strings, count returns the number of Unicode code points. For example,
é猫 has two code points. The letter e followed by a combining acute accent
also counts as two code points.
(empty? collection) → boolean
Returns whether count is zero. Lists, pointers, maps, strings, and null are accepted.
(first list) → value
Returns the first element or pointer segment, or null when empty.
(rest list) → list
Returns a fresh sequence without the first element. A pointer produces a
pointer; an empty list produces [] and an empty pointer produces the root pointer.
(rest [a b c]) (rest [a]) (rest []) (rest null) (rest [a null false]) (rest [a [b c] {name Ada}]) (let [list [a b c]] [(rest .list) .list])
[[b c] [] [] [] [null false] [[b c] {name Ada}] [[b c] [a b c]]]
{boolean (rest false) extra (rest [a b] 1) map (rest {a 1 b 2}) missing (rest) number (rest 2) string (rest abc)}
#cases [{output #err <mismatch type> select string} {output #err <mismatch type> select map} {output #err <mismatch type> select number} {output #err <mismatch type> select boolean} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select extra}]
(jsonptr text) → pointer
Converts RFC 6901 text to a pointer. The empty string gives the root pointer;
a nonempty string must start with / and contain only valid ~0 and ~1
escapes. Invalid text gives #err <invalid value>; a non-string gives
#err <mismatch type>.
(get root path) → value
(get root path default) → value
Returns the value selected by path in root, or default if an entry is missing.
Without default, a missing entry returns null.
A selected value of null or false is returned unchanged, not replaced by default.
get does not modify root or copy the selected value.
A missing key or out-of-range nonnegative list index returns the default, including during nested traversal.
An empty path returns root.
(get {name Ada} name) (get {settings {theme dark}} (jsonptr "/settings/theme")) (get {users [{name Ada} {name Bea}]} [users 1 name]) (get {} theme default)
Ada dark Bea default
(put root path value) → collection
Stores value at path in root in place and returns the same root collection.
Inserts or replaces a map entry, or replaces an existing list element.
A list index equal to its length appends one element. Larger indices are errors.
Values replace rather than merge, and null is stored rather than deleted.
Creates maps, never lists, for missing or null intermediate values.
Integer path segments require an existing list.
The root must be a map or list, not null.
An empty path is an error.
Path, type, and bounds errors leave the target unchanged.
value is stored without copying, so a collection reachable from more than one place shows the change in each.
Use collections/copy to change a copy instead.
(let [user {name Ada}] [(put .user name Bea) .user]) (put {settings {theme light}} (jsonptr "/settings/theme") dark) (put {users [{name Ada}]} [users 0 name] Bea)
[[{name Bea} {name Bea}] {settings {theme dark}} {users [{name Bea}]}]
(let [user {name Ada} changed (put (collections/copy .user) name Bea)] [.user .changed]) (let [root {settings {theme light}} changed (put (collections/copy .root) (jsonptr "/settings/theme") dark)] [.root .changed]) (let [root {users [{name Ada}]} changed (put (collections/copy .root shallow) [users 0 name] Bea)] [.root .changed]) (let [root {users [{name Ada}]} changed (put (collections/copy .root shallow) users [])] [.root .changed])
(let [user {name Ada} changed (put (collections/copy .user) name Bea)] [.user .changed]) (let [root {settings {theme light}} changed (put (collections/copy .root) (jsonptr "/settings/theme") dark)] [.root .changed]) (let [root {users [{name Ada}]} changed (put (collections/copy .root shallow) [users 0 name] Bea)] [.root .changed]) (let [root {users [{name Ada}]} changed (put (collections/copy .root shallow) users [])] [.root .changed])
[[{name Ada} {name Bea}] [{settings {theme light}} {settings {theme dark}}] [{users [{name Bea}]} {users [{name Bea}]}] [{users [{name Ada}]} {users []}]]
A change is visible through every value that shares the changed collection:
(let [original {profile {name Ada} settings {theme dark}} renamed (put (collections/copy .original shallow) profile {name Bea})] (put .renamed [settings theme] light) [.original .renamed])
(let [original {profile {name Ada} settings {theme dark}} renamed (put (collections/copy .original shallow) profile {name Bea})] (put .renamed [settings theme] light) [.original .renamed])
{profile {name Ada} settings {theme light}} {profile {name Bea} settings {theme light}}
(remove root path) → collection
Removes the entry selected by path from root in place and returns the same root collection.
Removes a map member or a list element, shifting later list elements left.
Empty parent collections are not removed.
A missing key, out-of-range nonnegative index, or missing or null intermediate value leaves root unchanged.
No containers are created.
The root must be a map or list, not null.
An empty path produces #err <invalid value>.
Path and type errors leave the target unchanged.
(let [m {a b}] [(remove .m a) .m]) (let [m {}] [(remove .m a) .m]) (let [m {a {b 1}}] [(remove .m (jsonptr "/a/b")) .m])
[[{} {}] [{} {}] [{a {}} {a {}}]]
(let [m {a 1 b 2} changed (remove (collections/copy .m) a)] [.m .changed]) (let [m {a {b 1 c 2}} changed (remove (collections/copy .m) (jsonptr "/a/b"))] [.m .changed]) (let [m {a {b 1 c 2}} changed (remove (collections/copy .m shallow) (jsonptr "/a/b"))] [.m .changed]) (let [xs [Ada Bea] changed (remove (collections/copy .xs) 0)] [.xs .changed])
(let [m {a 1 b 2} changed (remove (collections/copy .m) a)] [.m .changed]) (let [m {a {b 1 c 2}} changed (remove (collections/copy .m) (jsonptr "/a/b"))] [.m .changed]) (let [m {a {b 1 c 2}} changed (remove (collections/copy .m shallow) (jsonptr "/a/b"))] [.m .changed]) (let [xs [Ada Bea] changed (remove (collections/copy .xs) 0)] [.xs .changed])
[[{a 1 b 2} {b 2}] [{a {b 1 c 2}} {a {c 2}}] [{a {c 2}} {a {c 2}}] [[Ada Bea] [Bea]]]
(contains? root path) → boolean
Returns true if path selects a value in root, including null or false, otherwise false.
(contains? {x null} x) (contains? {<!x>} x) (contains? {} x)
(contains? {x null} x) (contains? <!x> x) (contains? {} x)
true true false
(contains? {a null} a) (contains? <!a> (jsonptr "/a")) (contains? {a {b 1}} [a b]) (contains? [a b] 1) (contains? {a 1} missing) (contains? {a {}} [a b]) (contains? [a b] 2) (contains? {a/b null} a/b) (contains? <!/a> ["/a"]) (contains? <!/a> (jsonptr "/~1a")) (contains? {users [{name null}]} [users 0 name]) (contains? {users [{name null}]} (jsonptr "/users/0/name")) (contains? {"" null} "") (contains? {} []) (contains? [] []) (contains? null a)
true true true true false false false true true true true true true true true false
{escaped-token (jsonptr "/~2") extra (contains? {} a false) list-key (contains? [a] ["0"]) map-index (contains? {a 1} [0.5]) missing (contains?) missing-path (contains? {}) negative-index (contains? [a] -1) path-type (contains? {a 1} true) scalar-intermediate (contains? {a 1} [a b])}
#cases [{output #err <mismatch type> select path-type} {output #err <invalid value> select negative-index} {output #err <invalid value> select escaped-token} {output #err <mismatch type> select map-index} {output #err <mismatch type> select list-key} {output #err <mismatch type> select scalar-intermediate} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select missing-path} {output #err <arity mismatch> select extra}]
(keys map) → list
Returns the map's string keys in ascending Unicode order.
(values map) → list
Returns map values in the same ascending key order used by keys.
(pairs map) → list
Returns [key value] pairs in the same ascending key order used by keys.
(pairs {a 2 z 1}) (pairs {}) (pairs {a null}) (pairs null) (pairs {<!""> /a 1 a/b 2})
[[[a 2] [z 1]] [] [[a null]] [] [["" false] ["/a" 1] [a/b 2]]]
{extra (pairs {} {}) list (pairs [a 1]) missing (pairs) string (pairs abc)}
#cases [{output #err <mismatch type> select list} {output #err <mismatch type> select string} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select extra}]
2.4.1.16. from-pairs ↑
Function(from-pairs list) → map
Returns a fresh map from [key value] pairs in list.
Later duplicate keys win.
(from-pairs [[b 2] [a 1]]) (from-pairs []) (from-pairs null) (from-pairs [[a 1] [b 2] [a 3]]) (from-pairs [[a 1] [a null] [b false]]) (from-pairs [["" 0] ["/a" 1] [a/b 2]]) (from-pairs [[nested {a 1}] [items [1 2]]]) (from-pairs (pairs {<!z> a null nested {b 2}})) (pairs (from-pairs [[z 1] [a 2] [z 3]]))
{a 1 b 2} {} {} {a 3 b 2} {<!b> a null} {"" 0 /a 1 a/b 2} {items [1 2] nested {a 1}} {<!z> a null nested {b 2}} [[a 2] [z 3]]
{empty-pair (from-pairs [[]]) extra (from-pairs [] []) later-invalid-pair (from-pairs [[a 1] [b]]) list-key (from-pairs [[[a] 1]]) long-pair (from-pairs [[a 1 2]]) map (from-pairs {a 1}) map-pair (from-pairs [{a 1}]) missing (from-pairs) non-pair (from-pairs [1]) null-key (from-pairs [[null a]]) null-pair (from-pairs [null]) number-key (from-pairs [[1 a]]) short-pair (from-pairs [[a]]) string (from-pairs abc) string-pair (from-pairs [ab])}
#cases [{output #err <mismatch type> select map} {output #err <mismatch type> select string} {output #err <mismatch type> select non-pair} {output #err <mismatch type> select map-pair} {output #err <mismatch type> select string-pair} {output #err <invalid value> select empty-pair} {output #err <invalid value> select null-pair} {output #err <invalid value> select short-pair} {output #err <invalid value> select long-pair} {output #err <mismatch type> select number-key} {output #err <mismatch type> select null-key} {output #err <mismatch type> select list-key} {output #err <invalid value> select later-invalid-pair} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select extra}]
2.5. Ordering ↑
These core functions compare values, sort lists, remove duplicates, and group values by key. They are part of the core library; see DUST for source notation, evaluation, and special forms.
2.5.1. Ordering, uniqueness, and grouping ↑
(compare first second) → integer
Returns -1, 0, or 1 when first sorts before, ties with, or sorts after second, using sort ordering.
(compare 2 5) (compare 5 5) (compare 5 2) (compare a b) (compare 猫 é) (compare false true) (compare true false) (compare null null) (compare 1.5 2.5) (compare -0.0 0.0) (compare #link 2 #link 1) (compare 9007199254740992 9007199254740993)
(compare 2 5) (compare 5 5) (compare 5 2) (compare a b) (compare 猫 é) (compare false true) (compare true false) (compare null null) (compare 1.5 2.5) (compare 0.0 0.0) (compare #link 2 #link 1) (compare 9007199254740992 9007199254740993)
-1 0 1 -1 1 -1 1 0 -1 0 1 -1
{extra-argument (compare 1 2 3) functions (compare (fn [x] .x) (fn [x] .x)) lists (compare [1] [1]) maps (compare {a 1} {a 1}) mixed-numbers (compare 1 1.0) mixed-scalars (compare a 1) no-arguments (compare) null-and-value (compare null 1) one-argument (compare 1)}
#cases [{output #err <mismatch type> select mixed-numbers} {output #err <mismatch type> select mixed-scalars} {output #err <mismatch type> select null-and-value} {output #err <mismatch type> select lists} {output #err <mismatch type> select maps} {output #err <mismatch type> select functions} {output #err <arity mismatch> select no-arguments} {output #err <arity mismatch> select one-argument} {output #err <arity mismatch> select extra-argument}]
(sort list) → list
(sort comparator list) → list
Sorts list in place, stably, and returns the same list, without copying its elements.
The list's order changes only after all comparisons succeed.
list must be a list, not null or a pointer.
By default, sorts in ascending order and requires all elements to have the same kind.
Orderable kinds are null, boolean, integer, float, string, link, UTC, duration,
SHA-256, and pointer. Pointer segments compare lexicographically, with an
index before a key; a shorter prefix sorts before its extension. Integers and
nominal integer payloads compare exactly. Strings
compare by Unicode code point order, booleans put false before true, and
SHA-256 values compare their bytes. Null values tie.
Equal values keep their input order. With default ordering, compound values, functions, expression forms, and mixed kinds are errors, including in singleton lists.
A supplied comparator receives two elements and returns a negative integer, zero, or a positive integer when the first sorts before, ties with, or sorts after the second.
Custom comparators can order any element kinds.
A non-function comparator produces #err <expected function>. A non-integer result produces #err <mismatch type>.
Comparator errors stop sorting and are returned.
The comparator must define a consistent order and must not change the input list or the values being compared.
Its call order and call count are not guaranteed.
(sort [3 1 2]) (sort [猫 é a]) (sort [false true false]) (sort [#link 2 #link 1]) (sort [null null])
[[1 2 3] [a é 猫] [false false true] [#link 1 #link 2] [null null]]
(let [xs [3 1 2]] [(sort .xs) .xs]) (let [xs []] [(sort .xs) .xs]) (let [xs [2 1 2]] [(sort .xs) .xs])
[[[1 2 3] [1 2 3]] [[] []] [[1 2 2] [1 2 2]]]
(sort (fn [a b] (- .b .a)) [1 3 2]) (sort (fn [a b] (- (get .a rank) (get .b rank))) [{id a rank 2} {id b rank 1} {id c rank 2}])
[[3 2 1] [{id b rank 1} {id a rank 2} {id c rank 2}]]
(distinct list) → list
Returns a fresh list retaining the first occurrence of each distinct typed
value. Collections compare structurally, with map member order ignored.
Kinds remain distinct: integer 1 and float 1.0 are separate values.
Functions compare by identity. Positive and negative floating zero compare equal.
(distinct [1 1.0 1 1.0]) (distinct [{a 1 b 2} {a 1 b 2} [a] [a]]) (distinct [null false null false]) (let [f (fn [] 1) g (fn [] 1)] (count (distinct [.f .f .g])))
[[1 1.0] [{a 1 b 2} [a]] [null false] 2]
2.5.2. Ordered comparisons ↑
The ordered comparisons accept the same scalar kinds as sort and require
matching kinds. They require at least two arguments and validate every
argument's kind before comparing adjacent pairs from left to right. As ordinary
functions, all argument expressions evaluate before comparison begins.
(< first second more*) → boolean
Returns whether the arguments are strictly increasing.
(<= first second more*) → boolean
Returns whether the arguments are nondecreasing.
(> first second more*) → boolean
Returns whether the arguments are strictly decreasing.
(>= first second more*) → boolean
Returns whether the arguments are nonincreasing.
2.6. Types ↑
These core functions test and convert values. They are part of the core library; see DUST for source notation, evaluation, and special forms.
2.6.1. Type Predicates ↑
(error? value) → boolean
Returns true if value is an error and false otherwise. Errors can be
passed as arguments without losing their tags.
[(error? #err app/oh-no) (error? (/ 1 0)) (error? (= 1 1.0)) (error? (=)) (let [inspect :error?] (inspect (/ 1 0))) (error? null) (error? false) (error? true) (error? 0) (error? 1.0) (error? "") (error? dust/type-mismatch) (error? "#err dust/type-mismatch") (error? []) (error? {}) (error? {error dust/type-mismatch}) (error? (fn [] null)) (error? `.name)]
(error? #err app/oh-no) (error? (/ 1 0)) (error? (= 1 1.0)) (error? (=)) (let [inspect :error?] (inspect (/ 1 0))) (error? null) (error? false) (error? true) (error? 0) (error? 1.0) (error? "") (error? dust/type-mismatch) (error? "#err dust/type-mismatch") (error? []) (error? {}) (error? {error dust/type-mismatch}) (error? (fn [] null)) (error? `.name)
true true true true true false false false false false false false false false false false false false
(null? x) → boolean
Returns true if x is null, otherwise false.
[(null? null) (null? nil) (null? "null") (null? false)]
(null? null) (null? nil) (null? "null") (null? false)
true false false false
(string? s) → boolean
Returns true if s is a string, otherwise false.
[(string? nil) (string? "") (string? "42") (string? null) (string? 42)]
(string? nil) (string? "") (string? "42") (string? null) (string? 42)
true true true false false
(int? n) → boolean
Returns true if n is an integer, otherwise false.
A link scalar is not an integer.
[(int? 0) (int? -42) (int? 1.0) (int? 1e0) (int? "42") (int? (+ 1 2))]
(int? 0) (int? -42) (int? 1.0) (int? 1.0) (int? "42") (int? (+ 1 2))
true true false false false true
(float? n) → boolean
Returns true if n is a floating-point number, otherwise false.
[(float? 1.0) (float? 1e0) (float? 1) (float? "1.0")]
(float? 1.0) (float? 1.0) (float? 1) (float? "1.0")
true true false false
(number? n) → boolean
Returns true if n is an integer or a float, otherwise false.
A link scalar is not a number.
[(number? 1) (number? 1.5) (number? "1") (number? true) (number? null)]
(number? 1) (number? 1.5) (number? "1") (number? true) (number? null)
true true false false false
(jsonptr? value) → boolean
Returns true for a pointer and false for other values. A pointer is not a
list, even though the list functions accept it.
(jsonptr? /a) ; true
(jsonptr? "/a") ; false
(list? xs) → boolean
Returns true if xs is a list, otherwise false.
[(list? []) (list? [1]) (list? {}) (list? null)]
(list? []) (list? [1]) (list? {}) (list? null)
true true false false
(map? m) → boolean
Returns true if m is a map, otherwise false.
[(map? {}) (map? {name Ada}) (map? []) (map? null)]
(map? {}) (map? {name Ada}) (map? []) (map? null)
true true false false
(boolean? b) → boolean
Returns true if b is either true or false, otherwise false.
[(boolean? true) (boolean? false) (boolean? null) (boolean? "true") (boolean? 0)]
(boolean? true) (boolean? false) (boolean? null) (boolean? "true") (boolean? 0)
true true false false false
(template? value) → boolean
Returns true if value is a string template, otherwise false.
(let [t (templates/from-parts {strings ["hello-" ""] values [Ada]})] [(template? .t) (string? .t) (template? "hello-Ada") (templates/parts .t) (str .t) (strings/starts-with? .t hello) (= .t "hello-Ada") (count .t) (strings/join [.t "!"] "") (compare .t "hello-Ada") (get {hello-Ada 42} .t)]) (templates/render (templates/from-parts {strings ["hello-" ""] values ["<Ada>"]}) {escape (strings/escape html-text)}) (let [name Ada] #template [["hello-" ""] [.name]]) #template [["outer-" ""] [#template [["inner-" ""] [Ada]]]]
(let [t (templates/from-parts {strings [hello- ""] values [Ada]})] [(template? .t) (string? .t) (template? hello-Ada) (templates/parts .t) (str .t) (strings/starts-with? .t hello) (= .t hello-Ada) (count .t) (strings/join [.t !] "") (compare .t hello-Ada) (get {hello-Ada 42} .t)]) (templates/render (templates/from-parts {strings [hello- ""] values ["<Ada>"]}) {escape (strings/escape html-text)}) (let [name Ada] ~"hello-,.name") ~"outer-,~"inner-,Ada""
[[true true false {strings [hello- ""] values [Ada]} hello-Ada true true 9 hello-Ada! 0 42] hello-<Ada> hello-Ada outer-inner-Ada]
(fn? f) → boolean
Returns true if f is a function, otherwise false.
[(fn? (fn [] null)) (fn? :+) (fn? +) (fn? [])]
(fn? (fn [] null)) (fn? :+) (fn? +) (fn? [])
true true false false
(local? v) → boolean
Returns true if v is a local reference, such as the quoted form
`.name, otherwise false.
[(local? `.name) (local? `:name) (local? ".name") (local? name)]
(local? `.name) (local? `:name) (local? ".name") (local? name)
true false false false
(const? v) → boolean
Returns true if v is a constant reference, such as the quoted form
`:name, otherwise false. A local and a constant with the same name are
not equal.
[(const? `:math/pi) (const? `.math/pi) (const? :math/pi) (const? ":math/pi")]
(const? `:math/pi) (const? `.math/pi) (const? :math/pi) (const? ":math/pi")
true false false false
2.6.2. Type Conversion ↑
(int value) → integer
Returns an integer unchanged, truncates a float toward zero, or parses a signed decimal string exactly. String input accepts an optional ASCII sign followed by ASCII decimal digits, including leading zeros. Whitespace, decimal points, exponents, and hexadecimal notation are rejected. Values outside signed 64-bit range are errors. Booleans, null, and nominal types such as links are rejected.
(int "9007199254740993") (int -1.9) (int +00042) (int "-9223372036854775808")
9007199254740993 -1 42 -9223372036854775808
(float value) → float
Converts an integer to a float, returns a float unchanged, or parses a signed
decimal string with an optional decimal point and exponent. Integer conversion
can lose precision; for example, integer 9007199254740993 converts to
9007199254740992.0. Whitespace, hexadecimal notation, NaN, and infinity are
rejected. Overflow is an error; underflow to zero is permitted. Booleans, null,
and nominal types such as links are rejected.
(str values*) → string
Converts zero or more values to strings and concatenates them in argument order.
It inserts no spaces or other separators.
With no arguments, returns the empty string.
Strings contribute their contents without added quotes or escaping.
A null argument contributes the empty string.
Numbers and booleans use their RON representations.
Lists, maps, and representable tagged values use canonical RON formatting.
RON has one canonical format; layout follows its normal formatting rules.
The formatter appends no terminal newline, and str uses those canonical bytes
unchanged. Internal line breaks in formatted collections are retained.
Collections are printed as complete values, not flattened into arguments.
Within collections, null is printed as null and strings are quoted when required to preserve their values.
str is an ordinary function.
Its arguments are evaluated before conversion.
String contents are text and are not interpreted as expressions.
(str) ; => ""
(str a null b) ; => "ab"
(str order- 42 .json) ; => "order-42.json"
(do [ (str [null "null" 1 true]) (str {z 2 a 1}) (str `.name) (str #utc 2026-10-24T12:34:56Z) ])
(do [(str [null "null" 1 true]) (str {a 1 z 2}) (str `.name) (str #utc 2026-10-24T12:34:56Z)])
""[null "null" 1 true]"" """ a 1 z 2 """ "#local name" "#utc 2026-10-24T12:34:56Z"
(boolean value) → boolean
Returns false if value is null or false, otherwise true.
(format template args*) → string
Returns a string by substituting args into the conversion specifications in template.
The syntax is printf-style. DUST supports %v for value conversion and
requires the argument types below.
A conversion has the form %[flags][width][.precision]conversion.
Each conversion consumes one argument in order, except %%, which consumes none.
Other text is copied unchanged.
The string template contains no embedded expressions.
| Conversion | Argument | Result |
|---|---|---|
s |
String | String contents |
v |
Any value | Text produced by str |
d, i |
Integer | Signed decimal |
o |
Integer | Unsigned octal |
x, X |
Integer | Unsigned hexadecimal, lowercase or uppercase |
f |
Float | Fixed-point decimal |
e, E |
Float | Scientific notation with e or E |
g, G |
Float | Compact decimal or scientific notation |
% |
None | A literal percent sign, written %% |
Width is a decimal minimum field width. Padding uses spaces on the left by default and never truncates a value. Width counts UTF-8 bytes, not code points or display columns.
| Flag | Applies to | Effect |
|---|---|---|
- |
All except % |
Left-align within the field |
+ |
Signed decimal and floats | Always include a sign |
| Space | Signed decimal and floats | Prefix nonnegative values with a space |
0 |
Numeric conversions | Pad with zeros after any sign or base prefix |
# |
o, x, X |
Alternate form with an octal or hexadecimal prefix |
# |
Floats | Keep the decimal point, and keep trailing zeros for g and G |
The + flag overrides the space flag, and - overrides 0.
Integer precision also overrides 0.
Flags not applicable to a conversion are errors.
Precision is a decimal count after ., with an omitted count meaning zero.
For integers it sets the minimum digit count.
For f, e, and E it sets the fractional digit count, defaulting to six.
For g and G it sets the significant digit count, defaulting to six, with zero treated as one.
These numeric conversions follow C printf formatting rules, including rounding and alternate forms.
Precision is not supported for s or v.
The %% escape accepts no flags, width, or precision.
Integer formatting preserves all 64 bits without conversion through a float.
Octal and hexadecimal conversions interpret negative integers modulo 2^64.
Floating-point formatting rounds the stored binary value, not an exact decimal value.
Numeric output uses . as the decimal point and no locale-specific grouping.
There is no implicit numeric conversion: %.2f requires a float, so integer 42 is an error.
The s conversion requires a string, while v accepts any value, including null as the empty string.
Dynamic width or precision (*), positional arguments ($), length modifiers,
and conversions not listed above are unsupported.
Invalid specifications produce #err <invalid value>.
An incompatible argument type, including a non-string template, produces #err <mismatch type>.
Missing or extra arguments produce #err <arity mismatch>.
Formatting, field widths, and precision are subject to the execution and output resource limits.
(format "Hello, %s!" Ada) (format "Invoice %04d: %.2f EUR" 7 12.5) (format "Progress: %d%%" 75) (format "Result: %v" {count 3})
"Hello, Ada!" "Invoice 0007: 12.50 EUR" "Progress: 75%" "Result: {count 3}"
2.7. Arithmetic ↑
These core functions provide arithmetic. They are part of the core library; see DUST for source notation, evaluation, and special forms.
2.7.1. Arithmetic ↑
Arithmetic proceeds from left to right.
Links are not numbers and are rejected by numeric operations.
Numbers never convert implicitly: the numeric operands of one call must be all
integers or all floats, and an integer paired with a float is a type mismatch.
Convert explicitly with float or int.
Integer overflow and non-finite floating-point results are errors.
The shared operators also support the explicit temporal combinations below. UTC and duration values retain their nominal types and signed 100 ns tick precision. A number never becomes an implicit duration.
| Operation | Result |
|---|---|
| UTC + duration; duration + UTC | UTC |
| Duration + duration | Duration |
| UTC - duration | UTC |
| UTC - UTC; duration - duration | Duration |
| Duration * integer; integer * duration | Duration |
| Duration / integer | Duration, truncated toward zero |
| Duration / duration | Float ratio |
Other temporal combinations are type errors. See the Time library for temporal examples and bounds.
(+) → integer
(+ value) → value
(+ value other more*) → value
Returns the sum of its arguments.
With no arguments, returns 0.
With one argument, returns that numeric or temporal value unchanged.
[(+) (+ 7) (+ 1 2 3) (+ 1.0) (+ 1.0 2.5) (+ -2 2)]
(+) (+ 7) (+ 1 2 3) (+ 1.0) (+ 1.0 2.5) (+ -2 2)
0 7 6 1.0 3.5 0
(- value more*) → value
With one argument, returns its negation. With additional arguments, subtracts each from the running result in order. At least one argument is required. Numeric operands follow the arithmetic rules above; temporal operands use the combinations in the table. Integer overflow and non-finite floating-point results are errors, including when negating the minimum integer. Unary subtraction also negates durations; UTC timestamps cannot be negated.
(- 3) (- 10 3 2) (- 10.0 2.5) (- -9007199254740993) (- 9007199254740995 1 1) (- 0)
-3 5 7.5 9007199254740993 9007199254740993 0
(*) → integer
(* value) → value
(* value other more*) → value
Returns the product of its arguments.
With no arguments, returns 1.
With one argument, returns that number or duration unchanged.
[(*) (* 7) (* 2 3 4) (* 1.0) (* 2.0 1.5) (* -2 0)]
(*) (* 7) (* 2 3 4) (* 1.0) (* 2.0 1.5) (* -2 0)
1 7 24 1.0 3.0 0
(/ n) → float
(/ value divisor more*) → value
Returns n divided by d and each additional divisor in more, or the reciprocal of n when called with one argument.
Numeric operands must be all integers or all floats. Numeric division proceeds from left to right and always returns a float, even for integer operands. Duration division follows the temporal combinations in the table above. A zero divisor is an error. A duration requires a divisor; unary duration reciprocals are not defined.
[(/ 2) (/ 6 3) (/ 1 2) (/ 24 2 3) (/ -2) (/ 1 3) (/ 1.0 2.0) (/ 0 2)]
(/ 2) (/ 6 3) (/ 1 2) (/ 24 2 3) (/ -2) (/ 1 3) (/ 1.0 2.0) (/ 0 2)
0.5 2.0 0.5 4.0 -0.5 0.3333333333333333 0.5 0.0
(rem dividend divisor) → number
Returns the remainder with the sign of the dividend. Two integers produce an
exact integer remainder; otherwise the result is a float. A zero divisor is an
error. The minimum integer divided by -1 has remainder zero.
3. Comparison Functions ↑
The comparison/ library provides additional equality and ordering operations.
3.1. Comparison functions ↑
3.1.1. comparison/not= ↑
Function(comparison/not= value more*) → boolean
Returns the negation of the corresponding equality call. This means that not all
arguments are equal, rather than requiring every pair to differ. One argument
returns false; no arguments is an error. Equality retains its matching-kind
checks and stops comparing after the first unequal pair.
(comparison/not= 1 2) (comparison/not= 1 1) (comparison/not= 1) (comparison/not= 1 2 x)
true false false true
3.1.2. comparison/identical? ↑
Function
Returns true if its operands refer to the same value, otherwise false.
Errors raised at the same place with the same tags and message may be the
same value.
(comparison/identical? hello "hello")
(comparison/identical? hello hello)
true
(let [es (map (fn [i] (/ .i 0)) [1 2])] (comparison/identical? (first .es) (lists/second .es))) (let [es (map (fn [s] (int .s)) ["x" "y"])] (comparison/identical? (first .es) (lists/second .es))) (let [es (map (fn [v] (/ 1 .v)) [0 "a"])] (comparison/identical? (first .es) (lists/second .es))) (comparison/identical? (/ 1 0) (/ 2 0)) (let [es (map (fn [i] (/ .i 0)) [1 2])] (= (first .es) (lists/second .es))) (map (fn [i] (/ .i 0)) [1 2])
(let [es (map (fn [i] (/ .i 0)) [1 2])] (comparison/identical? (first .es) (lists/second .es))) (let [es (map (fn [s] (int .s)) [x y])] (comparison/identical? (first .es) (lists/second .es))) (let [es (map (fn [v] (/ 1 .v)) [0 a])] (comparison/identical? (first .es) (lists/second .es))) (comparison/identical? (/ 1 0) (/ 2 0)) (let [es (map (fn [i] (/ .i 0)) [1 2])] (= (first .es) (lists/second .es))) (map (fn [i] (/ .i 0)) [1 2])
true true false false true [#err {<division integer zero> message "division by zero"} #err {<division integer zero> message "division by zero"}]
(let [a {x 1} b {x 1} c .a] (comparison/identical? .a .b))
(let [a {x 1} b {x 1} c .a] (comparison/identical? .a .b))
false
(let [a {x 1} b {x 1} c .a] (comparison/identical? .a .c))
(let [a {x 1} b {x 1} c .a] (comparison/identical? .a .c))
true
(let [f (fn [x] .x) g .f h (fn [x] .x)] (comparison/identical? .f .g))
(let [f (fn [x] .x) g .f h (fn [x] .x)] (comparison/identical? .f .g))
true
(let [f (fn [x] .x) g .f h (fn [x] .x)] (comparison/identical? .f .h))
(let [f (fn [x] .x) g .f h (fn [x] .x)] (comparison/identical? .f .h))
false
4. Function Utilities ↑
The fn/ library provides function composition and adaptation.
4.1. Function utilities ↑
4.1.1. fn/partial ↑
Function(fn/partial f args*) → function
Returns a function that calls f with args followed by its call-time arguments.
With no fixed arguments, returns f.
((fn/partial :+ 10) 1 2) ((fn/partial :- 10) 3) ((fn/partial :+ 1 2) 3) ((fn/partial :+ 1 2)) ((fn/partial :+) 1 2 3) ((fn/partial :+ 1 2 3 4) 5 6) ((fn/partial (fn [a b c] [.a .b .c]) null) false x) (= (fn/partial :fn/identity) :fn/identity)
13 7 6 3 6 21 [null false x] true
{function ((fn/partial 1 a) b) missing (fn/partial) too-few ((fn/partial :fn/identity)) too-many ((fn/partial :fn/identity a) b)}
#cases [{output #err <arity mismatch> select missing} {output #err <expected function> select function} {output #err <arity mismatch> select too-few} {output #err <arity mismatch> select too-many}]
(fn/comp) → function
(fn/comp f) → function
(fn/comp f g functions*) → function
Returns a function composing its inputs right to left, passing all call arguments to the rightmost function and each result to the next.
With no inputs, returns fn/identity.
With one, returns f.
((fn/comp :fn/identity :fn/identity) hello) ((fn/comp :count :rest) [a b c]) ((fn/comp (fn [x] (* .x 2)) (fn [x] (+ .x 1))) 3) ((fn/comp) [a b]) ((fn/comp :+) 1 2 3) ((fn/comp :fn/identity :+)) ((fn/comp (fn [x] (* .x 2)) :+) 1 2 3 4) ((fn/comp (fn [x] (* .x 2)) (fn [x] (+ .x 1)) (fn [x] (- .x 3))) 5) (= (fn/comp) :fn/identity) (= (fn/comp :+) :+)
hello 2 8 [a b] 6 0 20 6 true true
{identity-extra ((fn/comp) a b) identity-missing ((fn/comp)) inner-arity ((fn/comp :fn/identity :fn/identity) a b) inner-function ((fn/comp :fn/identity 1) a) outer-function ((fn/comp 1 :fn/identity) a)}
#cases [{output #err <arity mismatch> select identity-missing} {output #err <arity mismatch> select identity-extra} {output #err <expected function> select inner-function} {output #err <expected function> select outer-function} {output #err <arity mismatch> select inner-arity}]
4.1.3. fn/identity ↑
Function(fn/identity value) → value
Returns value unchanged.
(fn/identity null) (fn/identity false) (fn/identity [a b]) (fn/identity {a 1}) (fn/identity 0) (fn/identity hello) (= (fn/identity :fn/identity) :fn/identity) (let [list [a b]] (comparison/identical? (fn/identity .list) .list))
null false [a b] {a 1} 0 hello true true
{extra (fn/identity a b) missing (fn/identity)}
#cases [{output #err <arity mismatch> select missing} {output #err <arity mismatch> select extra}]
4.1.4. fn/constantly ↑
Function(fn/constantly value) → function
Returns a function that ignores its arguments and returns value.
((fn/constantly a)) ((fn/constantly false) 1 2) ((fn/constantly null) x) ((fn/constantly [a b]) 1 2 3 4 5) (let [value {a 1} f (fn/constantly .value)] (comparison/identical? (f) .value)) (let [f (fn/constantly x)] [(f) (f a) (f a b)])
a false null [a b] true [x x x]
{extra (fn/constantly a b) missing (fn/constantly)}
#cases [{output #err <arity mismatch> select missing} {output #err <arity mismatch> select extra}]
(fn/juxt f functions*) → function
Returns a function that applies f and functions to the same arguments and returns their results in order as a list.
((fn/juxt :first :lists/last) [a b c]) ((fn/juxt :count :first) [x y]) ((fn/juxt :fn/identity :fn/identity) null) ((fn/juxt :fn/identity) false) ((fn/juxt :+ :*)) ((fn/juxt :+ :- :*) 8 2) ((fn/juxt :+ :* :+ :*) 1 2 3 4) ((fn/juxt (fn [x] null) (fn [x] false)) a)
[[a c] [2 x] [null null] [false] [0 1] [10 6 16] [10 24 10 24] [null false]]
4.1.6. fn/complement ↑
Function(fn/complement f) → function
Returns a function that calls f with its arguments and negates the result as a boolean.
((fn/complement :empty?) []) ((fn/complement :empty?) [a]) ((fn/complement :fn/identity) false) ((fn/complement :fn/identity) 1) ((fn/complement :fn/identity) null) ((fn/complement :fn/identity) 0) ((fn/complement :fn/identity) []) ((fn/complement :fn/identity) "") ((fn/complement (fn [] null))) ((fn/complement :=) 1 1) ((fn/complement :=) 1 1 1 2)
false true true false true false false false true false true
{extra (fn/complement :fn/identity :fn/identity) missing (fn/complement)}
#cases [{output #err <arity mismatch> select missing} {output #err <arity mismatch> select extra}]
(fn/fnull f x) → function
(fn/fnull f x y) → function
(fn/fnull f x y z) → function
Returns a function that calls f, replacing null in its first three argument positions with the supplied defaults x, y, and z.
Preserves false and does not supply missing arguments.
((fn/fnull :+ 0) null 3) ((fn/fnull :+ 0 0) null null) ((fn/fnull :fn/identity true) false) ((fn/fnull (fn [a b c] [.a .b .c]) x y z) null null null) ((fn/fnull (fn [a b c] [.a .b .c]) x) null null false) ((fn/fnull (fn [a b] [.a .b]) x y z) null null) ((fn/fnull :fn/identity null) null) ((fn/fnull (fn [a b c d] [.a .b .c .d]) x y z) null false null null)
3 0 false [x y z] [x null false] [x y] null [x false z null]
{extra-default (fn/fnull :+ 0 0 0 0) missing (fn/fnull) missing-call-argument ((fn/fnull :+ 0)) missing-second-argument ((fn/fnull :+ 0 0) null) missing-second-of-three ((fn/fnull :+ 0 0 0) null) no-default (fn/fnull :fn/identity)}
#cases [{output #err <arity mismatch> select missing} {output #err <arity mismatch> select no-default} {output #err <arity mismatch> select extra-default} {output #err <arity mismatch> select missing-call-argument} {output #err <arity mismatch> select missing-second-argument} {output #err <arity mismatch> select missing-second-of-three}]
5. Boolean predicates ↑
The core provides predicates for boolean values.
(true? value) → boolean
Returns true if value is the boolean true, otherwise false.
(false? value) → boolean
Returns true if value is the boolean false, otherwise false.
6. Collection Functions ↑
The lists/ and maps/ namespaces provide operations for each collection type.
The collections/ namespace provides operations shared by lists and maps.
Functions that change a collection change it in place and return it; functions that make a new collection share nested values with their inputs. Unless stated otherwise, operations that make a new collection accept null as an empty collection, and operations that change a collection require a target of the stated type. Map keys are strings.
6.1. List functions: lists/ ↑
6.1.1. lists/mapcat ↑
Function(lists/mapcat f list lists*) → list
Applies f to corresponding elements of list and any additional lists, then concatenates the result lists one level deep.
Stops at the shortest input list.
(lists/mapcat (fn [x] [.x .x]) [a b]) (lists/mapcat (fn [x] [.x]) []) (lists/mapcat (fn [x] (if (= .x 0) [] [.x])) [0 1 0 2]) (lists/mapcat (fn [x] [.x]) null) (lists/mapcat (fn [x] null) [a b]) (lists/mapcat (fn [a b] [.a .b]) [1 2] [a]) (lists/mapcat (fn [a b c] [.a .b .c]) [1 2] [a b] [false null]) (lists/mapcat :fn/identity [[[a]] [[b]]]) (lists/mapcat :fn/identity [[null false] [] [0]])
[[a a b b] [] [1 2] [] [] [1 a] [1 a false 2 b null] [[a] [b]] [null false 0]]
{list (lists/mapcat :fn/identity abc) missing (lists/mapcat) result (lists/mapcat :fn/identity [1]) transducer (lists/mapcat :fn/identity)}
#cases [{output #err <mismatch type> select list} {output #err <mismatch type> select result} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select transducer}]
6.1.2. lists/keep ↑
Function(lists/keep f list) → list
Returns the results of applying f to each element of list, excluding null but preserving false and input order.
(lists/keep (fn [x] (if (comparison/identical? .x 0) null .x)) [0 1 false 2]) (lists/keep :fn/identity [null false 1]) (lists/keep :fn/identity []) (lists/keep :fn/identity null) (lists/keep :fn/identity [null null]) (lists/keep :fn/identity [false 0 "" [] {}]) (lists/keep (fn [x] (* .x 2)) [1 2 3]) (lists/keep (fn [x] [.x]) [a b])
[[1 false 2] [false 1] [] [] [] [false 0 "" [] {}] [2 4 6] [[a] [b]]]
{extra (lists/keep :fn/identity [a] [b]) list (lists/keep :fn/identity abc) missing (lists/keep) transducer (lists/keep :fn/identity)}
#cases [{output #err <mismatch type> select list} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select transducer} {output #err <arity mismatch> select extra}]
(lists/nth list index default?) → value
Returns the element at the nonnegative, zero-based integer index.
An out-of-range index returns default if supplied; otherwise it is an error.
A negative index is an error even when a default is supplied.
6.1.4. lists/second ↑
Function(lists/second list) → value
Returns the second element of list, or null if list has fewer than two elements.
(lists/second [a b c]) (lists/second [a]) (lists/second []) (lists/second null) (lists/second [a false]) (lists/second [a null c]) (lists/second [a {name Ada}]) (lists/second [a [b c]])
b null null null false null {name Ada} [b c]
{boolean (lists/second false) extra (lists/second [a b] fallback) map (lists/second {a 1 b 2}) missing (lists/second) number (lists/second 2) string (lists/second ab)}
#cases [{output #err <mismatch type> select string} {output #err <mismatch type> select map} {output #err <mismatch type> select number} {output #err <mismatch type> select boolean} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select extra}]
6.1.5. lists/last ↑
Function(lists/last list) → value
Returns the last element, or null for an empty list.
6.1.6. lists/append ↑
Function(lists/append list values*) → list
Appends values to list in place as individual elements and returns the same list.
Values are stored without copying. With no additional values, leaves list unchanged.
list must be a list, not null or a pointer.
(let [xs [a]] [(lists/append .xs b) .xs]) (let [xs []] [(lists/append .xs null false) .xs]) (let [xs [1 2]] [(lists/append .xs 3 4) .xs])
[[[a b] [a b]] [[null false] [null false]] [[1 2 3 4] [1 2 3 4]]]
(lists/append [1 2] 3 4) (lists/append [] null false) (let [xs [a]] [(lists/append (collections/copy .xs) b) .xs])
[[1 2 3 4] [null false] [[a b] [a]]]
6.1.7. lists/concat ↑
Function(lists/concat lists*) → list
Returns a fresh list containing the elements of each input list, in order. With no inputs, returns an empty list.
6.1.8. lists/interpose ↑
Function(lists/interpose separator list) → list
Returns a fresh list with separator between adjacent elements of list.
(lists/interpose x [a b c]) (lists/interpose x [a]) (lists/interpose x []) (lists/interpose x null) (lists/interpose null [a b]) (lists/interpose false [a b]) (lists/interpose [x y] [[a] [b]]) (let [list [a b]] [(lists/interpose x .list) .list])
[[a x b x c] [a] [] [] [a null b] [a false b] [[a] [x y] [b]] [[a x b] [a b]]]
{boolean (lists/interpose x false) extra-argument (lists/interpose x [a b] [c]) map (lists/interpose x {a 1}) missing-list (lists/interpose x) no-arguments (lists/interpose) string (lists/interpose x abc)}
#cases [{output #err <mismatch type> select string} {output #err <mismatch type> select map} {output #err <mismatch type> select boolean} {output #err <arity mismatch> select no-arguments} {output #err <arity mismatch> select missing-list} {output #err <arity mismatch> select extra-argument}]
6.1.9. lists/interleave ↑
Function(lists/interleave) → list
(lists/interleave list) → list
(lists/interleave list1 list2) → list
(lists/interleave list1 list2 lists*) → list
Returns a fresh list taking one element from each input list in turn, stopping at the shortest list.
Returns [] with no inputs and a shallow copy with one.
(lists/interleave [a b] [1 2]) (lists/interleave [a b c] [1]) (lists/interleave [a b] [1 2] [x y]) (lists/interleave [] [1 2]) (lists/interleave) (lists/interleave [a b]) (lists/interleave []) (lists/interleave null) (lists/interleave [a b] null) (lists/interleave [a] [1 2]) (lists/interleave [a b] [1 2] [x]) (lists/interleave [a b] [1 2] [x y] [true false]) (lists/interleave [null false] [[a] {b 2}]) (let [list [a b]] [(lists/interleave .list) .list])
[[a 1 b 2] [a 1] [a 1 x b 2 y] [] [] [a b] [] [] [] [a 1] [a 1 x] [a 1 x true b 2 y false] [null [a] false {b 2}] [[a b] [a b]]]
{additional (lists/interleave [1] [2] abc) boolean (lists/interleave false) first (lists/interleave abc [1]) map (lists/interleave {a 1}) number (lists/interleave 1) second (lists/interleave [1] abc) string (lists/interleave abc)}
#cases [{output #err <mismatch type> select string} {output #err <mismatch type> select map} {output #err <mismatch type> select number} {output #err <mismatch type> select boolean} {output #err <mismatch type> select first} {output #err <mismatch type> select second} {output #err <mismatch type> select additional}]
6.1.10. lists/flatten ↑
Function(lists/flatten list) → list
Returns a list of the non-list elements in list, recursively expanding nested lists in order.
Empty lists contribute no elements.
Non-list elements, including maps, strings, and null, are kept unchanged.
list must be a list, otherwise produces #err <mismatch type>.
Missing or extra arguments produce #err <arity mismatch>.
(lists/flatten [1 [2 [3]] [] 4]) (lists/flatten [1 null [2]]) (lists/flatten []) (lists/flatten [[] [[]] []]) (lists/flatten [1 2 3]) (lists/flatten [[a a] [false [true]]]) (lists/flatten [{items [1 [2]]} [ hello world #custom [3 [4]]] null])
[[1 2 3 4] [1 null 2] [] [] [1 2 3] [a a false true] [ {items [1 [2]]} hello world #custom [3 [4]] null]]
{boolean-input (lists/flatten false) extra-argument (lists/flatten [1] [2]) integer-input (lists/flatten 42) map-input (lists/flatten {items [1 2]}) missing-argument (lists/flatten) null-input (lists/flatten null) string-input (lists/flatten abc) tagged-input (lists/flatten #custom [1 2])}
#cases [{output #err <mismatch type> select null-input} {output #err <mismatch type> select string-input} {output #err <mismatch type> select map-input} {output #err <mismatch type> select integer-input} {output #err <mismatch type> select boolean-input} {output #err <mismatch type> select tagged-input} {output #err <arity mismatch> select missing-argument} {output #err <arity mismatch> select extra-argument}]
6.1.11. lists/slice ↑
Function(lists/slice list start end?) → list
Returns a fresh list from start up to, but excluding, end.
Indexes are nonnegative, zero-based integers. An omitted end means the list's
length. Indexes beyond the length are clamped, and an end before the start
produces an empty list.
(lists/slice [a b c] 1 3) (lists/slice [a b c] 1) (lists/slice [a b] 1 0) (lists/slice [a b] 0 9)
[[b c] [b c] [] [a b]]
6.1.12. lists/take ↑
Function(lists/take n list) → list
Returns a fresh list of the first n elements of list, or all elements if fewer are available.
(lists/take 2 [a b c d]) (lists/take 5 [a b]) (lists/take 0 [a b]) (lists/take 2 []) (lists/take -1 [a b]) (lists/take 2 null) (lists/take 1.5 [a b c]) (lists/take -1.5 [a b]) (lists/take 2 [null false c]) (let [list [a b c]] [(lists/take 2 .list) .list])
[[a b] [a b] [] [] [] [] [a b] [] [null false] [[a b] [a b c]]]
{count (lists/take two [a b]) extra (lists/take 1 [a] [b]) list (lists/take 1 abc) missing (lists/take) transducer (lists/take 1)}
#cases [{output #err <mismatch type> select count} {output #err <mismatch type> select list} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select transducer} {output #err <arity mismatch> select extra}]
6.1.13. lists/take-while ↑
Function(lists/take-while predicate list) → list
Returns the leading elements of list for which predicate is truthy, stopping at the first falsey result.
(lists/take-while (fn [x] (< .x 3)) [1 2 3 1]) (lists/take-while :fn/identity [1 false 2]) (lists/take-while :fn/identity []) (lists/take-while :fn/identity null) (lists/take-while :fn/identity [false 1]) (lists/take-while :fn/identity [0 "" [] null 1]) (lists/take-while (fn [x] true) [null false]) (lists/take-while (fn [x] (< .x 3)) [1 2 3 invalid])
[[1 2] [1] [] [] [] [0 "" []] [null false] [1 2]]
{extra (lists/take-while :fn/identity [a] [b]) list (lists/take-while :fn/identity abc) missing (lists/take-while) predicate (lists/take-while 1 [a]) transducer (lists/take-while :fn/identity)}
#cases [{output #err <expected function> select predicate} {output #err <mismatch type> select list} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select transducer} {output #err <arity mismatch> select extra}]
6.1.14. lists/drop ↑
Function(lists/drop n list) → list
Returns a fresh list excluding the first n elements of list.
(lists/drop 2 [a b c d]) (lists/drop 5 [a b]) (lists/drop 0 [a b]) (lists/drop 2 []) (lists/drop -1 [a b]) (lists/drop 2 null) (lists/drop 1.5 [a b c]) (lists/drop -1.5 [a b]) (lists/drop 1 [a null false]) (let [list [a b c]] [(lists/drop 1 .list) .list])
[[c d] [] [a b] [] [a b] [] [c] [a b] [null false] [[b c] [a b c]]]
{count (lists/drop two [a b]) extra (lists/drop 1 [a] [b]) list (lists/drop 1 abc) missing (lists/drop) transducer (lists/drop 1)}
#cases [{output #err <mismatch type> select count} {output #err <mismatch type> select list} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select transducer} {output #err <arity mismatch> select extra}]
6.1.15. lists/drop-while ↑
Function(lists/drop-while predicate list) → list
Returns list from the first element for which predicate is falsey, skipping the preceding elements.
(lists/drop-while (fn [x] (< .x 3)) [1 2 3 1]) (lists/drop-while :fn/identity [1 false 2]) (lists/drop-while :fn/identity []) (lists/drop-while :fn/identity null) (lists/drop-while :fn/identity [false 1]) (lists/drop-while :fn/identity [0 "" [] null 1]) (lists/drop-while (fn [x] true) [null false]) (lists/drop-while (fn [x] (< .x 3)) [1 2 3 invalid])
[[3 1] [false 2] [] [] [false 1] [null 1] [] [3 invalid]]
{extra (lists/drop-while :fn/identity [a] [b]) list (lists/drop-while :fn/identity abc) missing (lists/drop-while) predicate (lists/drop-while 1 [a]) transducer (lists/drop-while :fn/identity)}
#cases [{output #err <expected function> select predicate} {output #err <mismatch type> select list} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select transducer} {output #err <arity mismatch> select extra}]
6.1.16. lists/partition ↑
Function(lists/partition n list) → list
(lists/partition n step list) → list
Returns groups of up to n elements from list, starting step elements apart and including short trailing groups.
step defaults to n.
(lists/partition 2 [a b c d e]) (lists/partition 2 1 [a b c]) (lists/partition 3 []) (lists/partition 3 [a]) (lists/partition 2 3 [a b c d e f g]) (lists/partition 3 1 [a b c d]) (lists/partition 1 [null false [a]]) (lists/partition 2 null) (lists/partition 2 1 null) (lists/partition 9223372036854775807 [a b]) (lists/partition 2 9223372036854775807 [a b c])
[[[a b] [c d] [e]] [[a b] [b c] [c]] [] [[a]] [[a b] [d e] [g]] [[a b c] [b c d] [c d] [d]] [[null] [false] [[a]]] [] [] [[a b]] [[a b]]]
{empty-zero (lists/partition 0 []) extra (lists/partition 2 1 [] []) fractional-size (lists/partition 1.5 [a]) fractional-step (lists/partition 2 1.5 [a]) list (lists/partition 2 abc) missing (lists/partition) negative-size (lists/partition -1 [a]) negative-step (lists/partition 2 -1 [a]) transducer (lists/partition 2) zero-size (lists/partition 0 [a]) zero-step (lists/partition 2 0 [a])}
#cases [{output #err <invalid value> select zero-size} {output #err <invalid value> select negative-size} {output #err <invalid value> select zero-step} {output #err <invalid value> select negative-step} {output #err <invalid value> select empty-zero} {output #err <mismatch type> select fractional-size} {output #err <mismatch type> select fractional-step} {output #err <mismatch type> select list} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select transducer} {output #err <arity mismatch> select extra}]
6.1.17. lists/reverse ↑
Function(lists/reverse list) → list
Reverses list in place and returns the same list, without copying the elements.
list must be a list, not null or a pointer.
(let [xs [a b c]] [(lists/reverse .xs) .xs]) (let [xs []] [(lists/reverse .xs) .xs]) (let [xs [a]] [(lists/reverse .xs) .xs])
[[[c b a] [c b a]] [[] []] [[a] [a]]]
(lists/reverse [a b c]) (lists/reverse []) (lists/reverse [null false])
[[c b a] [] [false null]]
6.1.18. lists/filter ↑
Function(lists/filter predicate list) → list
Calls predicate on each element, from left to right, and returns the elements
whose results are truthy. Retains their input order.
(lists/filter :boolean [null false 0 a]) (lists/filter (fn [x] (= .x 2)) [1 2 2 3]) (lists/filter :boolean [])
[[0 a] [2 2] []]
6.1.19. lists/any? ↑
Function(lists/any? predicate list) → boolean
Returns true at the first truthy predicate result without visiting later
elements. Returns false for an empty list.
(lists/any? :boolean [null false a]) (lists/any? :boolean []) (lists/any? (fn [x] (if .x true (/ 1 0))) [true false])
true false true
6.1.20. lists/every? ↑
Function(lists/every? predicate list) → boolean
Returns false at the first falsey predicate result without visiting later
elements. Returns true for an empty list.
(lists/every? :boolean [0 a]) (lists/every? :boolean []) (lists/every? (fn [x] (if .x (/ 1 0) false)) [false true])
true true false
6.1.21. lists/some ↑
Function(lists/some f list) → value
Returns the first truthy result of applying f to elements of list, without visiting later elements, or null if none matches.
(lists/some (fn [x] (get .x name)) [{} {name Ada} {name Bea}]) (lists/some :fn/identity [false null 0 7]) (lists/some :fn/identity [false null]) (lists/some :fn/identity []) (lists/some :fn/identity null) (lists/some :fn/identity [null false [] a]) (lists/some :fn/identity [false "" a]) (lists/some (fn [x] (if (> .x 1) {found .x} null)) [0 1 2 3]) (lists/some (fn [x] (if (= .x 2) yes null)) [1 2 invalid])
Ada 0 null null null [] "" {found 2} yes
{extra (lists/some :fn/identity [a] [b]) function (lists/some 1 [a]) list (lists/some :fn/identity abc) missing (lists/some) missing-list (lists/some :fn/identity)}
#cases [{output #err <expected function> select function} {output #err <mismatch type> select list} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select missing-list} {output #err <arity mismatch> select extra}]
6.1.22. lists/find-first ↑
Function(lists/find-first predicate list) → value
(lists/find-first predicate list default) → value
Returns the first element of list for which predicate is truthy, or default if none matches.
The default is null.
(lists/find-first (fn [x] (get .x name)) [{} {name Ada} {name Bea}]) (lists/find-first (fn [x] (comparison/identical? .x false)) [null false true]) (lists/find-first :fn/identity [null false]) (lists/find-first :fn/identity [] fallback) (lists/find-first :fn/identity null) (lists/find-first :fn/identity null fallback) (lists/find-first :fn/identity [0 1]) (lists/find-first (fn [x] true) [null] fallback) (lists/find-first (fn [x] false) [a b] false)
{name Ada} false null fallback null fallback 0 null false
{extra (lists/find-first :fn/identity [] fallback extra) list (lists/find-first :fn/identity abc) missing (lists/find-first) missing-list (lists/find-first :fn/identity)}
#cases [{output #err <mismatch type> select list} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select missing-list} {output #err <arity mismatch> select extra}]
6.1.23. lists/range ↑
Function(lists/range end) → list
(lists/range start end step?) → list
Returns integers from start up to, but excluding, end, incrementing by
step. The defaults are zero for start and one for step. A negative step
produces a descending range. A zero step is an error. All arguments must be integers.
(lists/range 3) (lists/range 1 6 2) (lists/range 3 0 -1) (lists/range 3 0)
[[0 1 2] [1 3 5] [3 2 1] []]
6.1.24. lists/repeat ↑
Function(lists/repeat count value) → list
Returns a list containing value the specified number of times. count must
be a nonnegative integer. Values are reused rather than deep copied.
(lists/repeat 3 x) (lists/repeat 0 x) (lists/repeat 2 null)
[[x x x] [] [null null]]
6.1.25. lists/sort-by ↑
Function(lists/sort-by f list) → list
(lists/sort-by f comparator list) → list
Sorts list in place, stably, by the keys returned by f, and returns the same list.
The list's order changes only after key extraction and all comparisons succeed.
list must be a list, not null or a pointer.
Calls f once per element, from left to right, before making comparisons.
The key function must be an ordinary function and must not change the input list or its elements.
Uses sort ordering, or the supplied comparator applied to the keys.
Equal keys keep their input order. Elements are shared without copying.
Key-function errors stop processing and are returned.
(lists/sort-by :first [[b 1] [a 2] [b 3]]) (lists/sort-by :count [abc a ab]) (lists/sort-by :first []) (let [m {seen []}] [(lists/sort-by (fn [x] (patch .m {seen (lists/append (get .m seen) .x)}) .x) [3 2 1]) (get .m seen)])
(lists/sort-by :first [[b 1] [a 2] [b 3]]) (lists/sort-by :count [abc a ab]) (lists/sort-by :first []) (let [m {seen []}] [(lists/sort-by (fn [x] (patch .m {seen (lists/append (get .m seen) .x)}) .x) [3 2 1]) (get .m seen)])
[[[a 2] [b 1] [b 3]] [a ab abc] [] [[1 2 3] [3 2 1]]]
(let [xs [ab c def]] [(lists/sort-by :count .xs) .xs]) (let [xs []] [(lists/sort-by :count .xs) .xs]) (let [xs [ab cd e]] [(lists/sort-by :count .xs) .xs])
[[[c ab def] [c ab def]] [[] []] [[e ab cd] [e ab cd]]]
6.1.26. lists/partition-by ↑
Function(lists/partition-by f list) → list
Returns consecutive groups from list, starting a new group whenever f returns a different value.
(lists/partition-by :fn/identity [a a b a]) (lists/partition-by :fn/identity []) (lists/partition-by (fn [x] (math/zero? .x)) [0 0 1 2 0]) (lists/partition-by :fn/identity null) (lists/partition-by :fn/identity [null null false false null]) (lists/partition-by :fn/identity [[a] [a] [b]]) (lists/partition-by (fn [x] same) [a b c]) (lists/partition-by :fn/identity [1 1.0 1]) (lists/partition-by :fn/identity [a b c])
[[[a a] [b] [a]] [] [[0 0] [1 2] [0]] [] [[null null] [false false] [null]] [[[a] [a]] [[b]]] [[a b c]] [[1] [1.0] [1]] [[a] [b] [c]]]
{extra (lists/partition-by :fn/identity [a] [b]) function (lists/partition-by 1 [a]) list (lists/partition-by :fn/identity abc) missing (lists/partition-by) transducer (lists/partition-by :fn/identity)}
#cases [{output #err <expected function> select function} {output #err <mismatch type> select list} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select transducer} {output #err <arity mismatch> select extra}]
6.1.27. lists/group-by ↑
Function(lists/group-by function list) → map
Calls function on each element, from left to right. Each result must be a
string and becomes a map key. Each map value is the list of input elements
with that key, preserving their input order. Non-string keys are errors.
(lists/group-by :first [[a 1] [b 2] [a 3]]) (lists/group-by (fn [x] (strings/case upper .x)) [a A b]) (lists/group-by :first [])
{a [[a 1] [a 3]] b [[b 2]]} {A [a A] B [b]} {}
6.1.28. lists/frequencies ↑
Function(lists/frequencies strings) → map
Counts each string in the input list and returns a map from strings to integer
counts. Non-string elements are errors; integer 1 is never coerced to string "1".
(lists/frequencies [a b a]) (lists/frequencies []) (lists/frequencies [é 猫 é])
{a 2 b 1} {} {é 2 猫 1}
6.2. Map functions: maps/ ↑
6.2.1. maps/zipmap ↑
Function(maps/zipmap keys values) → map
Returns a fresh map pairing string keys with values, stopping at the shorter list.
Later duplicate keys win.
(maps/zipmap [a b] [1 2]) (maps/zipmap [a b c] [1]) (maps/zipmap [a a] [1 2]) (maps/zipmap [] [1]) (maps/zipmap [a] [1 2]) (maps/zipmap [a] []) (maps/zipmap null [1]) (maps/zipmap [a] null) (maps/zipmap [a b c] [null false [1 2]]) (maps/zipmap [a a] [1 null]) (maps/zipmap ["" "/a" a/b] [1 2 3])
{a 1 b 2} {a 1} {a 2} {} {a 1} {} {} {} {<!b> a null c [1 2]} {a null} {"" 1 /a 2 a/b 3}
{extra-argument (maps/zipmap [a] [1] [2]) integer-key (maps/zipmap [1] [a]) keys-not-list (maps/zipmap a [1]) missing-values (maps/zipmap [a]) no-arguments (maps/zipmap) null-key (maps/zipmap [null] [a]) values-not-list (maps/zipmap [a] 1)}
#cases [{output #err <mismatch type> select keys-not-list} {output #err <mismatch type> select values-not-list} {output #err <mismatch type> select integer-key} {output #err <mismatch type> select null-key} {output #err <arity mismatch> select no-arguments} {output #err <arity mismatch> select missing-values} {output #err <arity mismatch> select extra-argument}]
6.2.2. maps/select-keys ↑
Function(maps/select-keys map keys) → map
Returns a fresh map containing the literal top-level keys in keys that are present in map.
(maps/select-keys {a 1 b 2} [b missing]) (maps/select-keys {a null} [a a]) (maps/select-keys null [a])
{b 2} {a null} {}
(maps/select-keys {a 1 b 2 c 3} [a c]) (maps/select-keys {/a 1 b 2} ["/a"]) (maps/select-keys {a 1} [missing a]) (maps/select-keys {/a/b 2 a {b 1}} ["/a/b"]) (maps/select-keys {<!b> a null c 3} [b a a]) (maps/select-keys null [a]) (maps/select-keys {a 1} []) (maps/select-keys {a 1} null)
{a 1 c 3} {/a 1} {a 1} {/a/b 2} {<!b> a null} {} {} {}
{extra (maps/select-keys {} [] []) key-type (maps/select-keys {a 1} [0]) keys (maps/select-keys {a 1} a) map (maps/select-keys [a] [a]) missing (maps/select-keys) missing-keys (maps/select-keys {}) path-list (maps/select-keys {a {b 1}} [[a b]])}
#cases [{output #err <mismatch type> select map} {output #err <mismatch type> select keys} {output #err <mismatch type> select key-type} {output #err <mismatch type> select path-list} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select missing-keys} {output #err <arity mismatch> select extra}]
6.3. Collection paths: collections/ ↑
6.3.1. collections/update ↑
Function(collections/update root path f args*) → collection
Replaces the value at path in root with the result of calling f on that value and args, in place, and returns the same root collection.
Stores the result as put does, including container creation and list growth.
A missing entry supplies null to f.
Validates the destination before calling f once, and again before storing the result.
The callback must be an ordinary function, otherwise produces #err <expected function>.
A callback error is returned without storing a replacement.
The callback receives the existing value without copying, and its side effects are not undone.
The root must be a map or list, not null.
An empty path produces #err <invalid value>.
(let [record {score 2}] (collections/update .record score :+ 3) .record) (let [record {user {score 2}}] (collections/update .record [user score] :+ 3) .record) (let [record {tags [old]}] (collections/update .record tags :lists/append new) .record)
{score 5} {user {score 5}} {tags [old new]}
(let [record {score 2} updated (collections/update (collections/copy .record) score :+ 3)] [.record .updated]) (let [record {user {score 2}} updated (collections/update (collections/copy .record) [user score] :+ 3)] [.record .updated]) (let [record {user {score 2}} updated (collections/update (collections/copy .record shallow) [user score] :+ 3)] [.record .updated])
[[{score 2} {score 5}] [{user {score 2}} {user {score 5}}] [{user {score 5}} {user {score 5}}]]
6.3.2. collections/copy ↑
Function(collections/copy value) → value
(collections/copy value shallow) → value
Returns a copy of value that changes to either leave the other unchanged.
Copies every list and map reachable from value. A list or map reached more than once is copied once, so collections shared within value stay shared within the copy, and cycles stay cycles.
With the mode shallow, copies only the top-level list or map; its members are shared.
Other values do not change and are returned as they are.
A mode other than shallow produces #err <invalid value>, and a mode that is not a string #err <mismatch type>.
Nesting deeper than the depth limit produces #err <limit resource>.
(let [m {a {b 1}} c (collections/copy .m)] (put .c ["a" "b"] 2) [.m .c]) (let [m {a {b 1}} c (collections/copy .m shallow)] (put .c ["a" "b"] 2) (put .c "x" 3) [.m .c]) (let [l [1 [2]] c (collections/copy .l)] (lists/append .c 3) (lists/append (get .c 1) 4) [.l .c]) (let [s {n 1} m {a .s b .s} c (collections/copy .m)] (put .c ["a" "n"] 2) [(get .c ["b" "n"]) (get .m ["a" "n"])]) [(collections/copy 5) (collections/copy "x") (collections/copy null)] (collections/copy {a 1} deep) (collections/copy {a 1} 5) (let [m {a 1}] (put .m "self" .m) (let [c (collections/copy .m)] [(comparison/identical? (get .c "self") .c) (comparison/identical? (get .c "self") .m)]))
(let [m {a {b 1}} c (collections/copy .m)] (put .c [a b] 2) [.m .c]) (let [m {a {b 1}} c (collections/copy .m shallow)] (put .c [a b] 2) (put .c x 3) [.m .c]) (let [l [1 [2]] c (collections/copy .l)] (lists/append .c 3) (lists/append (get .c 1) 4) [.l .c]) (let [s {n 1} m {a .s b .s} c (collections/copy .m)] (put .c [a n] 2) [(get .c [b n]) (get .m [a n])]) [(collections/copy 5) (collections/copy x) (collections/copy null)] (collections/copy {a 1} deep) (collections/copy {a 1} 5) (let [m {a 1}] (put .m self .m) (let [c (collections/copy .m)] [(comparison/identical? (get .c self) .c) (comparison/identical? (get .c self) .m)]))
[[{a {b 1}} {a {b 2}}] [{a {b 2}} {a {b 2} x 3}] [[1 [2]] [1 [2 4] 3]] [2 1] [5 x null] #err {<invalid value> message "unknown copy mode"} #err {<mismatch type> message "copy mode must be a string"} [true false]]
7. Hash Functions ↑
The hash/ library computes digests of DUST values.
7.1. Hash values ↑
#hash holds a digest together with the algorithm that produced it. It uses
RON's #prefix value notation, and its payload is itself a tagged element: the
tag names the algorithm and its value is the digest in lowercase hexadecimal.
#hash #sha256 ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
{"#hash": {"#sha256": "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"}}
Import validates the payload: the algorithm tag must name one hash/digest defines,
and the digest must be lowercase hexadecimal of that algorithm's length.
Invalid payloads produce #err <invalid tag>; once readers use the cell
decoders, an invalid payload is a parse error, invalid tag, at the tag.
#hash #sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
#hash #sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
#hash #sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Two #hash values are equal when both the algorithm and the digest are equal,
so digests of the same input under different algorithms are unequal rather
than a type mismatch. A #hash value never equals a string.
7.2. hash/digest ↑
Function(hash/digest algorithm value) → hash
Returns a #hash of value under algorithm, a name from this table:
| Algorithm | Digest | Kind |
|---|---|---|
sha256 |
32 bytes | SHA-2, cryptographic |
sha512 |
64 bytes | SHA-2, cryptographic |
sha3-256 |
32 bytes | SHA-3, cryptographic |
sha3-512 |
64 bytes | SHA-3, cryptographic |
xxh3-64 |
8 bytes | XXH3 64-bit, non-cryptographic |
xxh3-128 |
16 bytes | XXH3 128-bit, non-cryptographic |
Every DUST value can be hashed. The digest covers the value's canonical DUST form: the bytes the DUST formatter writes for it, encoded as UTF-8, without the single terminal file newline. Internal newlines and newlines inside string contents are kept. Arguments evaluate before hashing, and a quoted form hashes its resulting data. The canonical form follows the formatter exactly:
- A top-level list of two or more forms omits its brackets, so
(hash/digest sha256 [1 2])digests the bytes of1and2on separate lines; a one-element list keeps its brackets, and an empty list is empty. - Maps keep their braces and sort their keys, so maps with the same members hash identically regardless of member order.
- Integer
1and float1.0have different forms and digests. #utcand#durpayloads use their canonical spelling, so every spelling of the same instant or duration hashes identically.- Comments are excluded: documenting a form never changes its digest.
- A function hashes as its canonical form: a closure as its
fnsource, and a library function as its constant reference, such as:strings/case. Values captured by a closure are not part of its form. - Unicode text is not normalized.
A form whose known tag has an invalid payload, such as #link true, is
rejected when it is read, so it cannot reach hash/digest.
The string abc has canonical form abc, so its digests match the standard
test vectors for those three bytes, while the empty string has canonical form
"":
| Call | Result |
|---|---|
(hash/digest sha256 abc) |
#hash #sha256 ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad |
(hash/digest sha3-256 abc) |
#hash #sha3-256 3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532 |
(hash/digest xxh3-64 abc) |
#hash #xxh3-64 78af5f94892f3950 |
The XXH3 variants are for keys, bucketing, deduplication, and change detection,
never for integrity against an adversary. xxh3-128 makes accidental
collisions negligible where 64 bits are too few, such as content addressing. An unknown algorithm produces
#err <algorithm hash unknown>.
(hash/digest sha256 null) (hash/digest sha256 abc) (hash/digest sha256 "") (hash/digest sha256 1) (hash/digest sha256 1.0) (hash/digest sha256 [é 猫]) (hash/digest sha256 `.name) (hash/digest sha256 ``1) (hash/digest sha256 #link 1) (hash/digest sha256 #utc 1970-01-01T00:00:00Z) (hash/digest sha256 #dur PT1S) (hash/digest sha256 []) (hash/digest sha256 {a 1 b 2}) (hash/digest sha256 #utc 1970-01-01T00:00:00.0000001Z) (hash/digest sha256 #dur PT0.0000001S)
#hash #sha256 74234e98afe7498fb5daf1f36ac2d78acc339464f950703b8c019892f982b90b #hash #sha256 ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad #hash #sha256 12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126 #hash #sha256 6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b #hash #sha256 d0ff5974b6aa52cf562bea5921840c032a860a91a3512f7fe8f768f6bbe005f6 #hash #sha256 251304c0f0e98b997852859fc1e885ec256ad7845938663a88f5983d237f385c #hash #sha256 468f0dd394cbd3e8506524aa28faec25309cd9704c695aea1f9218e4f395b283 #hash #sha256 d135669e0056d96659a6969caa40dad4576a10121eaad744521e9a09cdca6a83 #hash #sha256 e64ae69a6b69823b59a98efc4eee9036bfd5dd2b4c171bd6b040bb649cd6d5e3 #hash #sha256 e6a04092b64d10308bda75ec4bbf7e5e9459b42fb8f2a425e319e304c445b078 #hash #sha256 9db6bc3f734529a972f45f22b2620c5151e35c2e5c5ec5714b31a55e4c6d4ff5 #hash #sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 #hash #sha256 53a6fbfea11bafdfe07457e9d925dedda5ca93811d422d4af322379990194d45 #hash #sha256 69211abbe894a3a02fce9a6a6d217ec326d939097bda56b2621500f826569af8 #hash #sha256 85fcc02259a4615aee0271b48d9ecae27a581a0e50d2550fabc4ac15e5e220b0
(hash/digest sha512 abc) (hash/digest sha3-256 abc) (hash/digest sha3-512 abc) (hash/digest xxh3-64 abc) (hash/digest xxh3-128 abc)
#hash #sha512 ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f #hash #sha3-256 3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532 #hash #sha3-512 b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0 #hash #xxh3-64 78af5f94892f3950 #hash #xxh3-128 06b05ab6733a618578af5f94892f3950
(= (hash/digest sha256 {a 1 b 2}) (hash/digest sha256 {a 1 b 2})) (comparison/not= (hash/digest sha256 1) (hash/digest sha256 1.0)) (comparison/not= (hash/digest sha256 é) (hash/digest sha256 é))
true true true
(comparison/not= (hash/digest sha256 abc) (hash/digest sha3-256 abc)) (comparison/not= (hash/digest xxh3-64 abc) (hash/digest xxh3-128 abc)) (= (hash/digest xxh3-64 abc) #hash #xxh3-64 78af5f94892f3950)
true true true
(hash/digest sha256 (fn [] 1))
#hash #sha256 06d0dff99f70155bfd64b168de5c36cede11e9c14ddf87358cbb89ea9f08ce69
(hash/digest sha256 [(fn [] 1)])
#hash #sha256 b6ca49a321d2250ae8533a020a3fbf07d9942b3af2e67728b342ed4b73f86d0e
8. Random Functions ↑
The random/ library makes reproducible random values from a seed. It reads no
host entropy: random/seeded always takes an explicit seed, and the same seed
gives the same values on every host and in every later version.
8.1. Generators ↑
A generator is a mutable value that the runtime owns. Each call that takes a
generator advances it in place, as regex/find advances a matcher. Two calls
on the same generator give the next two values of one sequence:
(let [g (random/seeded 42)]
[(random/int .g 0 6) (random/int .g 0 6)])
; [0 2] on every run
The algorithm is xoshiro256**, seeded through SplitMix64: the generator's four 64-bit state words are the first four SplitMix64 outputs for the seed. This choice is fixed.
A generator in a result is written as its algorithm and its current state, 64 lowercase hexadecimal digits for the four state words in order, each most significant digit first:
#rng {xoshiro256ss "9d1c68b67d1ceb4323acb714c3a78d019d431c524cad8dc572e82a070b0b9ddb"}
The state written is the one the generator has when the result is exported,
after the whole evaluation, not when the generator is placed in the result. So
[.g (random/int .g 0 6)] shows in its first slot the state after the
random/int call.
A #rng value read back from a document, or bound from the host, becomes a
new generator that continues the same sequence. It changes into a generator in
place when a random function first uses it, so every reference to it shares
one state. A payload that is not one xoshiro256ss member with 64 lowercase
hexadecimal digits, or whose state is all zero, produces #err <invalid tag>
when a random function uses it. Two generators are equal when their states are
equal.
(let [g (random/seeded 42)] [(random/int .g 0 6) (random/int .g 0 6) .g])
(let [g (random/seeded 42)] [(random/int .g 0 6) (random/int .g 0 6) .g])
0 2 #rng {xoshiro256ss 9d1c68b67d1ceb4323acb714c3a78d019d431c524cad8dc572e82a070b0b9ddb}
(let [g #rng {xoshiro256ss "9d1c68b67d1ceb4323acb714c3a78d019d431c524cad8dc572e82a070b0b9ddb"}] [(random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) .g])
(let [g #rng {xoshiro256ss 9d1c68b67d1ceb4323acb714c3a78d019d431c524cad8dc572e82a070b0b9ddb}] [(random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) .g])
4 5 5 #rng {xoshiro256ss 7e3fedbea92a13a5c9a25ba0f11c828cc38346747039f414cf55c271f2386fa5}
(random/int #rng {xoshiro256ss "00"} 0 6) (random/int #rng {xoshiro256ss "0000000000000000000000000000000000000000000000000000000000000000"} 0 6) (random/int #rng {xoshiro256ss "6DB07C7DD404690B81DDC5FE6C15769825CFE223490D9D1F9252543D113B0C36"} 0 6) (random/int #rng {pcg32 "6db07c7dd404690b81ddc5fe6c15769825cfe223490d9d1f9252543d113b0c36"} 0 6) (random/int #rng 42 0 6) (random/int #rng {xoshiro256ss "bdd732262feb6e9528efe333b266f10347526757130f9f52581ce1ff0e4ae394" extra true} 0 6)
(random/int #rng {xoshiro256ss 00} 0 6) (random/int #rng {xoshiro256ss 0000000000000000000000000000000000000000000000000000000000000000} 0 6) (random/int #rng {xoshiro256ss 6DB07C7DD404690B81DDC5FE6C15769825CFE223490D9D1F9252543D113B0C36} 0 6) (random/int #rng {pcg32 6db07c7dd404690b81ddc5fe6c15769825cfe223490d9d1f9252543d113b0c36} 0 6) (random/int #rng 42 0 6) (random/int #rng {<extra> xoshiro256ss bdd732262feb6e9528efe333b266f10347526757130f9f52581ce1ff0e4ae394} 0 6)
#err {<invalid tag> message "#rng requires a xoshiro256ss state of 64 hex digits, not all zero"} #err {<invalid tag> message "#rng requires a xoshiro256ss state of 64 hex digits, not all zero"} #err {<invalid tag> message "#rng requires a xoshiro256ss state of 64 hex digits, not all zero"} #err {<invalid tag> message "#rng requires a xoshiro256ss state of 64 hex digits, not all zero"} #err {<invalid tag> message "#rng requires a xoshiro256ss state of 64 hex digits, not all zero"} #err {<invalid tag> message "#rng requires a xoshiro256ss state of 64 hex digits, not all zero"}
Ranges are half-open: lo is included and hi is not. An empty range, where
lo > hi=, produces #err <invalid value>. A bound of the wrong type, or a
value that is not a generator, produces #err <mismatch type>.
8.2. random/seeded ↑
Function(random/seeded seed) → generator
Returns a new generator for the integer seed. Every integer is a seed,
negative ones included.
| Call | Result |
|---|---|
(random/seeded 42) |
#rng {xoshiro256ss bdd732262feb6e9528efe333b266f10347526757130f9f52581ce1ff0e4ae394} |
(random/seeded 0) |
#rng {xoshiro256ss e220a8397b1dcdaf6e789e6aa1b965f406c45d188009454ff88bb8a8724c81ec} |
(random/seeded 1.5) |
#err <mismatch type> |
(random/seeded 42) (random/seeded 0) (random/seeded -1) (random/seeded -9223372036854775808)
#rng {xoshiro256ss bdd732262feb6e9528efe333b266f10347526757130f9f52581ce1ff0e4ae394} #rng {xoshiro256ss e220a8397b1dcdaf6e789e6aa1b965f406c45d188009454ff88bb8a8724c81ec} #rng {xoshiro256ss e4d971771b652c20e99ff867dbf682c9382ff84cb27281e96d1db36ccba982d2} #rng {xoshiro256ss 481ec0a212a9f3dbc46fa638a630901261a685ffc80a8140592e268383e356f9}
(let [a (random/seeded 7) b (random/seeded 7)] [(random/int .a 0 100) (random/int .b 0 100) (random/int .a 0 100) (random/int .b 0 100)])
(let [a (random/seeded 7) b (random/seeded 7)] [(random/int .a 0 100) (random/int .b 0 100) (random/int .a 0 100) (random/int .b 0 100)])
70 70 27 27
(random/seeded 1.5) (random/seeded abc) (random/seeded) (random/int 42 0 6) (random/int #other {xoshiro256ss "6db07c7dd404690b81ddc5fe6c15769825cfe223490d9d1f9252543d113b0c36"} 0 6)
(random/seeded 1.5) (random/seeded abc) (random/seeded) (random/int 42 0 6) (random/int #other {xoshiro256ss 6db07c7dd404690b81ddc5fe6c15769825cfe223490d9d1f9252543d113b0c36} 0 6)
#err {<mismatch type> message "random/seeded requires an integer seed"} #err {<mismatch type> message "random/seeded requires an integer seed"} #err {<arity mismatch> message "wrong number of arguments"} #err {<mismatch type> message "expected a random generator"} #err {<mismatch type> message "expected a random generator"}
8.3. random/int ↑
Function(random/int generator lo hi) → integer
Returns an integer x with lo < x < hi=, uniform over the range. The range
can span all 64-bit integers. Integers use Lemire's multiply-and-reject method,
so no value is more likely than another.
Call, on (random/seeded 42) |
Result |
|---|---|
(random/int .g 0 6) |
0 |
(random/int .g -10 -5) |
-10 |
(random/int .g -9223372036854775808 9223372036854775807) |
-7676373272452217067 |
(random/int .g 5 5) |
#err <invalid value> |
(let [g (random/seeded 42)] [(random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6)]) (let [g (random/seeded 42)] [(random/int .g -10 -5) (random/int .g 5 6) (random/int .g -1000000000000000000 1000000000000000000)]) (let [g (random/seeded 7)] [(random/int .g 0 3) (random/int .g 0 3) (random/int .g 0 3) (random/int .g 0 3) (random/int .g 0 3)])
(let [g (random/seeded 42)] [(random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6)]) (let [g (random/seeded 42)] [(random/int .g -10 -5) (random/int .g 5 6) (random/int .g -1000000000000000000 1000000000000000000)]) (let [g (random/seeded 7)] [(random/int .g 0 3) (random/int .g 0 3) (random/int .g 0 3) (random/int .g 0 3) (random/int .g 0 3)])
[[0 2 4 5 5 4 4 5 4 3] [-10 5 360086822056278777] [2 0 2 2 2]]
(let [g (random/seeded 42)] [(random/int .g -9223372036854775808 9223372036854775807) (random/int .g -9223372036854775808 9223372036854775807) (random/int .g -9223372036854775808 9223372036854775807)]) (let [g (random/seeded 42)] [(random/int .g -1000000000000000000 1000000000000000000) (random/int .g -1000000000000000000 1000000000000000000) (random/int .g -1000000000000000000 1000000000000000000)])
(let [g (random/seeded 42)] [(random/int .g -9223372036854775808 9223372036854775807) (random/int .g -9223372036854775808 9223372036854775807) (random/int .g -9223372036854775808 9223372036854775807)]) (let [g (random/seeded 42)] [(random/int .g -1000000000000000000 1000000000000000000) (random/int .g -1000000000000000000 1000000000000000000) (random/int .g -1000000000000000000 1000000000000000000)])
[[-7676373272452217067 -2232420343890232707 3321214725393783200] [-832274057880235478 -242039498674662721 360086822056278777]]
(let [g #rng {xoshiro256ss "0000000000000001000000000000000000000000000000010000000000000001"}] [(random/int .g 0 6) .g]) (let [g #rng {xoshiro256ss "0000000000000001000000000000000000000000000000010000000000000001"} warm (random/seeded 7)] (for [i (lists/range 48)] (random/int .warm 0 2)) [(random/int .g 0 6) .g])
(let [g #rng {xoshiro256ss 0000000000000001000000000000000000000000000000010000000000000001}] [(random/int .g 0 6) .g]) (let [g #rng {xoshiro256ss 0000000000000001000000000000000000000000000000010000000000000001} warm (random/seeded 7)] (for [i (lists/range 48)] (random/int .warm 0 2)) [(random/int .g 0 6) .g])
[[0 #rng {xoshiro256ss 0000000004000080000020000400000040000000040000000010000004000000}] [0 #rng {xoshiro256ss 0000000004000080000020000400000040000000040000000010000004000000}]]
(random/int (random/seeded 1) 5 5) (random/int (random/seeded 1) 6 0) (random/int (random/seeded 1) 9223372036854775807 -9223372036854775808)
#err {<invalid value> message "random range is empty: lo must be below hi"} #err {<invalid value> message "random range is empty: lo must be below hi"} #err {<invalid value> message "random range is empty: lo must be below hi"}
(random/int (random/seeded 1) 0.0 6.0) (random/int (random/seeded 1) 0 6.5) (random/int (random/seeded 1) a z) (random/int (random/seeded 1) 6)
#err {<mismatch type> message "random/int requires integer bounds"} #err {<mismatch type> message "random/int requires integer bounds"} #err {<mismatch type> message "random/int requires integer bounds"} #err {<arity mismatch> message "wrong number of arguments"}
8.4. random/float ↑
Function(random/float generator lo hi) → float
Returns a float x with lo < x < hi=. Both bounds are floats. The function
takes the top 53 bits of the next output as u in [0, 1), and maps it to lo +
u * (hi - lo). When hi - lo is too large for a float, it uses lo * (1 - u) +
hi * u. A result that rounds up to hi is drawn again.
Call, on (random/seeded 42) |
Result |
|---|---|
(random/float .g 0.0 1.0) |
0.08386297105988216 |
(random/float .g -2.5 2.5) |
-2.080685144700589 |
(random/float .g 0 1) |
#err <mismatch type> |
(let [g (random/seeded 42)] [(random/float .g 0.0 1.0) (random/float .g 0.0 1.0) (random/float .g 0.0 1.0)]) (let [g (random/seeded 42)] [(random/float .g -2.5 2.5) (random/float .g 100.0 100.5) (random/float .g -1.0e308 1.0e308)]) (let [g (random/seeded 7)] [(random/float .g 0.0 1.0e-300) (random/float .g -1.0 0.0)])
(let [g (random/seeded 42)] [(random/float .g 0.0 1.0) (random/float .g 0.0 1.0) (random/float .g 0.0 1.0)]) (let [g (random/seeded 42)] [(random/float .g -2.5 2.5) (random/float .g 100.0 100.5) (random/float .g -1e+308 1e+308)]) (let [g (random/seeded 7)] [(random/float .g 0.0 1e-300) (random/float .g -1.0 0.0)])
[[0.08386297105988216 0.3789802506626686 0.6800434110281394] [-2.080685144700589 100.18949012533133 3.600868220562787e+307] [7.0057648217968965e-301 -0.7212487705262157]]
(random/float (random/seeded 1) 1.0 1.0) (random/float (random/seeded 1) 2.0 1.0)
#err {<invalid value> message "random range is empty: lo must be below hi"} #err {<invalid value> message "random range is empty: lo must be below hi"}
(random/float (random/seeded 1) 0 1) (random/float (random/seeded 1) 0.0 1) (random/float (random/seeded 1) a b)
#err {<mismatch type> message "random/float requires float bounds"} #err {<mismatch type> message "random/float requires float bounds"} #err {<mismatch type> message "random/float requires float bounds"}
8.5. random/choose ↑
Function(random/choose generator list) → value
Returns one item of list, each with the same chance. An empty list or
null produces #err <invalid value>.
Call, on (random/seeded 42) |
Result |
|---|---|
(random/choose .g [a b c]) |
a |
(random/choose .g [only]) |
only |
(random/choose .g []) |
#err <invalid value> |
(let [g (random/seeded 42)] [(random/choose .g [a b c]) (random/choose .g [a b c]) (random/choose .g [a b c]) (random/choose .g [a b c])]) (let [g (random/seeded 42)] [(random/choose .g [only]) (random/choose .g [1 2.5 "three" {four 4}])]) (let [g (random/seeded 7)] (random/choose .g [[1 2] [3 4]]))
(let [g (random/seeded 42)] [(random/choose .g [a b c]) (random/choose .g [a b c]) (random/choose .g [a b c]) (random/choose .g [a b c])]) (let [g (random/seeded 42)] [(random/choose .g [only]) (random/choose .g [1 2.5 three {four 4}])]) (let [g (random/seeded 7)] (random/choose .g [[1 2] [3 4]]))
[[a b c c] [only 2.5] [3 4]]
(random/choose (random/seeded 1) []) (random/choose (random/seeded 1) null)
#err {<invalid value> message "random/choose requires a nonempty list"} #err {<invalid value> message "random/choose requires a nonempty list"}
(random/choose (random/seeded 1) {a 1}) (random/choose (random/seeded 1) abc) (random/choose [a b])
#err {<mismatch type> message "expected a list or null"} #err {<mismatch type> message "expected a list or null"} #err {<arity mismatch> message "wrong number of arguments"}
8.6. random/shuffle ↑
Function(random/shuffle generator list) → list
Returns a new list with the items of list in a uniform random order, and
leaves list unchanged. The order comes from a Fisher–Yates shuffle that
swaps each position i, from the last down to the second, with a position
from 0 to i. null shuffles to [].
Call, on (random/seeded 7) |
Result |
|---|---|
(random/shuffle .g [1 2 3 4 5]) |
[1 5 3 2 4] |
(random/shuffle .g []) |
[] |
(random/shuffle .g {a 1}) |
#err <mismatch type> |
(let [g (random/seeded 7)] [(random/shuffle .g [1 2 3 4 5]) (random/shuffle .g [1 2 3 4 5]) (random/shuffle .g [a b c d e f g h])]) (let [g (random/seeded 7) xs [1 2 3]] [(random/shuffle .g .xs) .xs]) (let [g (random/seeded 7)] [(random/shuffle .g []) (random/shuffle .g [only]) (random/shuffle .g null)])
(let [g (random/seeded 7)] [(random/shuffle .g [1 2 3 4 5]) (random/shuffle .g [1 2 3 4 5]) (random/shuffle .g [a b c d e f g h])]) (let [g (random/seeded 7) xs [1 2 3]] [(random/shuffle .g .xs) .xs]) (let [g (random/seeded 7)] [(random/shuffle .g []) (random/shuffle .g [only]) (random/shuffle .g null)])
[[[1 5 3 2 4] [2 3 1 4 5] [g a c e f h b d]] [[2 1 3] [1 2 3]] [[] [only] []]]
(random/shuffle (random/seeded 1) {a 1}) (random/shuffle (random/seeded 1) abc) (random/shuffle [1 2 3])
#err {<mismatch type> message "expected a list or null"} #err {<mismatch type> message "expected a list or null"} #err {<arity mismatch> message "wrong number of arguments"}
9. String Functions ↑
The String library provides strings/ functions for Unicode text. Character counts
and indices count Unicode code points, starting at zero. Substring end indices
are excluded. A combining mark counts separately from the character before it.
Operations do not normalize text unless their documentation says so. Searches
match literal text rather than patterns or regular expressions.
Case conversion and word boundaries recognize Unicode letters and digits. Case conversion is locale independent. Character classification and full case mappings use Unicode 17.0.0, including expanding mappings and the contextual Greek final sigma rule.
9.1. String functions ↑
9.1.1. strings/case ↑
Function(strings/case case value) → string
(strings/case cases value) → string
Converts value with one case selector or a list of selectors. A list applies
the conversions from left to right. An empty list returns the input unchanged.
The selector and input must be strings. Unknown selectors produce an error.
| Selector | Conversion |
|---|---|
upper |
Unicode uppercase |
lower |
Unicode lowercase |
camel |
Words joined in camel case |
pascal |
Words joined in Pascal case |
snake |
Lowercase words joined with underscores |
kebab |
Lowercase words joined with hyphens |
title |
Capitalized words joined with spaces |
screaming-snake |
Uppercase words joined with underscores |
case-fold |
Unicode default full case folding |
Case conversion can change the number of code points: straße becomes
STRASSE with upper. Case folding is for caseless matching. It does not
normalize text or use Turkic-specific mappings.
Naming conversions split words at separators and case transitions. An acronym
followed by a capitalized word is split: HTTPServer contains HTTP and
Server. (strings/case [snake upper] HTTPServer) returns HTTP_SERVER.
(strings/case upper straße) (strings/case lower HELLO) (strings/case camel HTTP_server) (strings/case title hELLO_world) (strings/case case-fold Straße)
STRASSE hello httpServer "Hello World" strasse
(strings/case [snake upper] HTTPServer) (strings/case [upper lower] Straße) (strings/case [] Hello) (strings/case [camel snake] hello_world)
HTTP_SERVER strasse Hello hello_world
9.1.2. strings/trim ↑
Function(strings/trim opts s) → string
(strings/trim opts text s) → string
Returns s with whitespace or text removed from the ends selected by opts.
opts is a map tagged with exactly one of left, right, or both, optionally with repeat, as in {<both repeat>}.
Whitespace trimming removes full Unicode whitespace runs.
Text trimming removes one exact match per end, or repeats with repeat.
Both ends are processed left first.
Empty text leaves s unchanged.
(strings/trim <both> "\t \nalpha beta\r ") (strings/trim <both> hi ) (strings/trim <both> hi ) (strings/trim <both> "") (strings/trim <both> "\t\n") (strings/trim <both> hi) (strings/trim <both> "hi ")
"alpha beta" hi hi "" "" hi hi
(strings/trim <left> ha hahaha) (strings/trim <both> ha hahaha) (strings/trim <both repeat> ha hahaha) (strings/trim <both> aba ababa) (strings/trim <both repeat> aba ababa) (strings/trim <left repeat> ha hahaxhaha) (strings/trim <repeat right> ha hahaxhaha) (strings/trim <both repeat> ha hahaxhahaxhaha) (strings/trim <left repeat> ab aabb) (strings/trim <both> abc abc) (strings/trim <both repeat> abc ab) (strings/trim <both repeat> "" abc) (strings/trim <both repeat> "" "") (strings/trim <both repeat> a "") (strings/trim <both repeat> 🙂 🙂🙂x🙂🙂) (strings/trim <both repeat> é Éxé) (strings/trim <both repeat> é éxé) (strings/trim <both> " " " x ") (strings/trim <both repeat> " " " x ")
haha ha "" ba ba xhaha hahax xhahax aabb "" ab abc "" "" x Éx éxé " x " x
{both-left (strings/trim <both left> abc) both-right (strings/trim <both right> abc) conflicting-sides (strings/trim <left right> abc) extra (strings/trim <both> a abc extra) missing (strings/trim) missing-side (strings/trim {} abc) old-arity (strings/trim abc) opts-list (strings/trim [both] abc) opts-null (strings/trim null abc) opts-string (strings/trim both abc) repeat-only (strings/trim <repeat> abc) source-null (strings/trim <both> null) source-number (strings/trim <left> 1) text-null (strings/trim <left> null abc) text-number (strings/trim <right> 1 abc) text-options (strings/trim <all both> a abc) text-opts-null (strings/trim null a abc) text-source-null (strings/trim <right> a null) text-source-number (strings/trim <both repeat> a 1) unknown-option (strings/trim <all both> abc)}
#cases [{output #err <invalid value> select missing-side} {output #err <invalid value> select repeat-only} {output #err <invalid value> select conflicting-sides} {output #err <invalid value> select both-left} {output #err <invalid value> select both-right} {output #err <invalid value> select unknown-option} {output #err <invalid value> select text-options} {output #err <mismatch type> select opts-string} {output #err <mismatch type> select opts-list} {output #err <mismatch type> select opts-null} {output #err <mismatch type> select text-opts-null} {output #err <mismatch type> select source-null} {output #err <mismatch type> select source-number} {output #err <mismatch type> select text-source-null} {output #err <mismatch type> select text-source-number} {output #err <mismatch type> select text-null} {output #err <mismatch type> select text-number} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select old-arity} {output #err <arity mismatch> select extra}]
9.1.3. strings/split ↑
Function(strings/split string separator) → list
(strings/split string separator limit) → list
Splits at each literal occurrence of separator, preserving empty fields.
An empty separator splits into Unicode code points; splitting an empty string
with an empty separator returns a list containing the empty string.
An optional positive limit permits at most that many parts and keeps the
unsplit remainder in the final part. A zero limit removes trailing empty
parts. A negative limit preserves all parts, including trailing empty strings.
Omitting limit preserves trailing empty parts.
(strings/split "a,b,c" ",") (strings/split é猫🙂 "") (strings/split a.b. ".") (strings/split "" ",") (strings/split "" "")
[[a b c] [é 猫 🙂] [a b ""] [""] []]
9.1.4. strings/join ↑
Function(strings/join strings separator) → string
Joins a list of strings with separator between consecutive elements.
An empty list produces the empty string. Elements and the separator must be strings.
9.1.5. strings/replace ↑
Function(strings/replace string search replacement) → string
Replaces non-overlapping literal occurrences of search, from left to right.
An empty search inserts replacement before, between, and after the input's
Unicode code points: (strings/replace ab "" x) returns xaxbx.
(strings/replace banana na x) (strings/replace ab "" x) (strings/replace é猫 "" 🙂) (strings/replace a.b. "." 猫)
baxx xaxbx 🙂é🙂猫🙂 a猫b猫
9.1.6. strings/substring ↑
Function(strings/substring string start) → string
(strings/substring string start end) → string
Returns the code points from start up to, but excluding, end.
Without end, returns the rest of the string. Both indices must be integers
between zero and the string length, and start must not exceed end. Other
indices produce #err <bounds index>.
(strings/substring Aé猫Z 1 3) (strings/substring é猫🙂 1 3) (strings/substring é猫 2 2) (strings/substring é猫🙂 1)
é猫 猫🙂 "" 猫🙂
9.1.7. strings/starts-with? ↑
Function(strings/starts-with? string prefix) → boolean
Returns whether the string begins with the literal prefix.
Every string starts with the empty string.
(strings/starts-with? abc a) (strings/starts-with? é猫 é) (strings/starts-with? é猫 猫) (strings/starts-with? é猫 "")
true true false true
9.1.8. strings/ends-with? ↑
Function(strings/ends-with? string suffix) → boolean
Returns whether the string ends with the literal suffix.
Every string ends with the empty string.
(strings/ends-with? abc c) (strings/ends-with? é猫 猫) (strings/ends-with? é猫 é) (strings/ends-with? é猫 "")
true true false true
9.1.9. strings/includes? ↑
Function(strings/includes? string search) → boolean
Returns whether the string contains the literal search.
Every string includes the empty string.
(strings/includes? a.b ".") (strings/includes? é猫🙂 猫) (strings/includes? é猫 z) (strings/includes? é猫 "") (strings/includes? é é)
true true false true false
9.1.10. strings/split-lines ↑
Function(strings/split-lines s) → list
Returns the lines of s separated by LF or CRLF, without their line endings.
Trailing empty lines are omitted. A lone CR is ordinary content. An empty string produces a list containing one empty string. A nonempty string containing only line endings produces an empty list.
[(strings/split-lines "a\nb\r\nc\n") (strings/split-lines "") (strings/split-lines "\n\r\n") (strings/split-lines "a\rb") (strings/split-lines "a\n\nb") (strings/split-lines "a b")]
(strings/split-lines """ a b\r c """ ) (strings/split-lines "") (strings/split-lines """ \r """ ) (strings/split-lines "a\rb") (strings/split-lines """ a b """ ) (strings/split-lines a b)
[[a b c] [""] [] [ "a\rb"] [a "" b] [a b]]
9.1.11. strings/index-of ↑
Function(strings/index-of s value) → integer or null
(strings/index-of s value start) → integer or null
Returns the first index of the string value in s at or after start, or null if not found.
The integer start defaults to zero.
Negative starts are treated as zero.
A start beyond the string length finds no nonempty value.
An empty value matches at the start clamped to the range from zero through the string length.
Indices are not narrowed to a host's smaller integer type.
[(strings/index-of banana ana) (strings/index-of banana ana 2) (strings/index-of banana ana -3) (strings/index-of banana ana 9) (strings/index-of "🙂a🙂b" b) (strings/index-of "é" "é") (strings/index-of ABC a) (strings/index-of abc "" 99) (strings/index-of abc "" -9) (strings/index-of "" "") (strings/index-of abc b 9223372036854775807) (strings/index-of abc "" 9223372036854775807)]
(strings/index-of banana ana) (strings/index-of banana ana 2) (strings/index-of banana ana -3) (strings/index-of banana ana 9) (strings/index-of 🙂a🙂b b) (strings/index-of é é) (strings/index-of ABC a) (strings/index-of abc "" 99) (strings/index-of abc "" -9) (strings/index-of "" "") (strings/index-of abc b 9223372036854775807) (strings/index-of abc "" 9223372036854775807)
1 3 1 null 3 null null 3 0 0 null 3
9.1.12. strings/last-index-of ↑
Function(strings/last-index-of s value) → integer or null
(strings/last-index-of s value start) → integer or null
Returns the last index of the string value in s at or before start, or null if not found.
The integer start defaults to the string length.
Starts beyond the string length are treated as the string length.
A negative start returns null, including when value is empty.
An empty value matches at any index from zero through the string length.
The start limits the beginning of a match, not its end.
Indices are not narrowed to a host's smaller integer type.
[(strings/last-index-of banana ana) (strings/last-index-of banana ana 2) (strings/last-index-of banana ana -1) (strings/last-index-of banana ana 9223372036854775807) (strings/last-index-of abc abc 1) (strings/last-index-of abc "") (strings/last-index-of abc "" -1) (strings/last-index-of abc "" 99) (strings/last-index-of "🙂a🙂b" "🙂") (strings/last-index-of "é" "é") (strings/last-index-of "" "")]
(strings/last-index-of banana ana) (strings/last-index-of banana ana 2) (strings/last-index-of banana ana -1) (strings/last-index-of banana ana 9223372036854775807) (strings/last-index-of abc abc 1) (strings/last-index-of abc "") (strings/last-index-of abc "" -1) (strings/last-index-of abc "" 99) (strings/last-index-of 🙂a🙂b 🙂) (strings/last-index-of é é) (strings/last-index-of "" "")
3 1 null 3 0 3 null 3 2 null 0
9.1.13. strings/replace-first ↑
Function(strings/replace-first s match replacement) → string
Returns s with the first occurrence of match replaced by replacement.
Both match and replacement are literal strings.
An empty match inserts replacement at the start.
Returns s unchanged when there is no match.
[(strings/replace-first abab ab X) (strings/replace-first aaa aa X) (strings/replace-first "a.b.a" "." "$1") (strings/replace-first ab "" "-") (strings/replace-first "🙂x" "" "-") (strings/replace-first "" "" "+") (strings/replace-first abc x q) (strings/replace-first a a aa) (strings/replace-first "$1" "$1" "\\")]
(strings/replace-first abab ab X) (strings/replace-first aaa aa X) (strings/replace-first a.b.a "." $1) (strings/replace-first ab "" -) (strings/replace-first 🙂x "" -) (strings/replace-first "" "" +) (strings/replace-first abc x q) (strings/replace-first a a aa) (strings/replace-first $1 $1 "\\")
Xab Xa a$1b.a -ab -🙂x + abc aa "\\"
9.1.14. strings/blank? ↑
Function(strings/blank? s) → boolean
Returns true if s is null, empty, or contains only whitespace, otherwise false.
Other non-string values produce #err <mismatch type>.
[(strings/blank? null) (strings/blank? "") (strings/blank? " \t\r\n") (strings/blank? " ") (strings/blank? " ") (strings/blank? " ") (strings/blank? " ") (strings/blank? "") (strings/blank? " a ")]
(strings/blank? null) (strings/blank? "") (strings/blank? " \t\r\n") (strings/blank? ) (strings/blank? ) (strings/blank? ) (strings/blank? ) (strings/blank? ) (strings/blank? " a ")
true true true true true true true false false
9.1.15. String templates ↑
A template retains literal strings and evaluated values. It is accepted as a
string by string operations; template? distinguishes it from an
ordinary string. Rendering joins the literal pieces with the string forms of
the values.
(let [name Ada]
(templates/parts ~"hello-,.name"))
; => {strings [hello- ""] values [Ada]}
(let [name Ada]
(template? ~"hello-,.name"))
; => true
(let [name Ada]
~"hello-,.name")
; => hello-Ada
(let [name Ada]
(str hello- .name))
; => hello-Ada
Use the template to embed forms in a string. Use str to concatenate values.
(let [name Ada] [(= ~"hello-,.name" (str hello- .name)) (template? ~"hello-,.name") (template? (str hello- .name))])
true true false
templates/from-parts constructs a template from a map with strings
and values lists. The strings list must have one more item than values.
(templates/from-parts {strings [hello- ""] values [Ada]})
; => hello-Ada
templates/render renders a template. Its optional escape function
receives each evaluated embedded value before it is rendered; literal pieces
pass through unchanged.
For example, escape a query value without escaping its literal parameter prefix:
(let [term "cats & dogs"]
(templates/render
~"search=,.term"
{escape (strings/escape url-query-value)}))
; => "search=cats%20%26%20dogs"
Use the corresponding unescape function to decode an escaped string.
Passing a template to str or strings/join renders its evaluated values.
The result is an ordinary string.
Map literal keys are strings and are not evaluated. To create a key from a template, pass it as a value to a map operation:
(let [name Ada]
(put {} ~"hello-,.name" 1))
; => {hello-Ada 1}
9.1.16. strings/escape ↑
Function(strings/escape mode text) → string
(strings/escape mode) → function
Escapes text for the specified mode. With only a mode, returns a unary function
that escapes its argument, for use as a template's escape option. The function
prints and hashes as its call, such as (strings/escape html-text), and
renders a template argument before escaping it.
(strings/escape json-pointer "a/b~") ; => a~1b~0
(strings/escape url-query-value "a&b c") ; => a%26b%20c
(strings/escape url-form-field "a&b c") ; => a%26b+c
(strings/escape csv "a,b") ; => "\"a,b\""
(let [escape-html (strings/escape html-text)]
(.escape-html "<Alice>")) ; => "<Alice>"
Modes are json, dust, ron, csv, json-pointer, html-text,
html-attribute, url-path-segment, url-query-value, and url-form-field.
JSON Pointer mode escapes one segment; URL modes escape one component; CSV mode
escapes one field. The json, dust, and ron modes escape the contents of
a double-quoted string without adding quotes. HTML attribute mode also escapes
quotation marks. URL form fields encode spaces as +; the other URL modes use
%20. An unknown mode produces #err <invalid value>.
[(strings/escape html-attribute "&<>\"'")
(strings/escape html-attribute "<b>Tom & 'Sue'</b>")
(strings/escape html-attribute "&")
(strings/escape html-attribute "\\$1🙂")
(strings/escape html-attribute "")]
(strings/escape html-attribute &<>"')
(strings/escape html-attribute "<b>Tom & 'Sue'</b>")
(strings/escape html-attribute &)
(strings/escape html-attribute "\\$1🙂")
(strings/escape html-attribute "")
&<>"'
"<b>Tom & 'Sue'</b>"
&amp;
"\\$1🙂"
""
(strings/escape json-pointer "a/b~")
(strings/unescape json-pointer "a~1b~0")
(strings/escape url-query-value "a&b c")
(strings/escape url-form-field "a&b c")
(strings/unescape url-form-field "a%26b+c")
(strings/escape csv "a,b")
(strings/unescape csv "\"a,b\"")
(strings/escape url-form-field "*~ ")
(strings/escape json-pointer a/b~)
(strings/unescape json-pointer a~1b~0)
(strings/escape url-query-value "a&b c")
(strings/escape url-form-field "a&b c")
(strings/unescape url-form-field a%26b+c)
(strings/escape csv "a,b")
(strings/unescape
csv
"""
"a,b"
"""
)
(strings/escape url-form-field "*~ ")
a~1b~0
a/b~
a%26b%20c
a%26b+c
"a&b c"
"""
"a,b"
"""
"a,b"
*%7E+
(strings/escape json "a\"b\n")
(strings/unescape json "\\uD83D\\uDE00")
(strings/unescape ron (strings/escape ron "a\"b\n"))
(strings/unescape dust (strings/escape dust "a\\b"))
(strings/unescape json "\\q")
(strings/unescape url-query-value "%2G")
(strings/unescape json-pointer "~2")
(strings/escape
json
"""
a"b
"""
)
(strings/unescape json "\\uD83D\\uDE00")
(strings/unescape
ron
(strings/escape
ron
"""
a"b
"""
))
(strings/unescape dust (strings/escape dust a\b))
(strings/unescape json "\\q")
(strings/unescape url-query-value %2G)
(strings/unescape json-pointer "~2")
""a\u005c"b\\n""
😀
"""
a"b
"""
a\b
#err {<invalid value>
message "invalid string escape"}
#err {<invalid value>
message "invalid URL percent escape"}
#err {<invalid value>
message "invalid JSON Pointer escape"}
(strings/escape html-text "<a & b>")
(strings/escape html-attribute "\"<a>\"")
(strings/unescape html-text "<a & b>")
(strings/unescape html-attribute "😀 "x"")
(strings/unescape html-text "© ≂̸")
(strings/escape html-text "<a & b>")
(strings/escape
html-attribute
"""
"<a>"
"""
)
(strings/unescape html-text "<a & b>")
(strings/unescape html-attribute "😀 "x"")
(strings/unescape html-text "© ≂̸")
"<a & b>"
"<a>"
"<a & b>"
""😀 "x"""
"© ≂̸"
(strings/escape url-path-segment "a/b")
(strings/unescape url-path-segment a%2Fb)
(let [escape-html (strings/escape html-text)] (.escape-html "<Alice>"))
(let [decode (strings/unescape html-text)] (.decode "<Alice>"))
(let [name "<Alice>"] (templates/render ~"<p>,.name,</p>" {escape (strings/escape html-text)}))
(fn? (strings/escape html-text))
(strings/escape unknown x)
(strings/unescape unknown x)
(strings/escape 1 x)
(strings/escape html-text 1)
((strings/escape html-text) x y)
(strings/escape url-path-segment a/b)
(strings/unescape url-path-segment a%2Fb)
(let [escape-html (strings/escape html-text)] (.escape-html "<Alice>"))
(let [decode (strings/unescape html-text)] (.decode <Alice>))
(let [name "<Alice>"] (templates/render ~"<p>,.name,</p>" {escape (strings/escape html-text)}))
(fn? (strings/escape html-text))
(strings/escape unknown x)
(strings/unescape unknown x)
(strings/escape 1 x)
(strings/escape html-text 1)
((strings/escape html-text) x y)
a%2Fb
a/b
<Alice>
"<Alice>"
"<p><Alice></p>"
true
#err <invalid value>
#err <invalid value>
#err <mismatch type>
#err <mismatch type>
#err <arity mismatch>
(let [escape-html (strings/escape html-text)]
[(.escape-html "<Alice>")
(fn? .escape-html)
(.escape-html ~"<,(str b),>")
(hash/digest sha256 .escape-html)])
(let
[escape-html (strings/escape html-text)]
[(.escape-html "<Alice>") (fn? .escape-html)
(.escape-html ~"<,(str b),>") (hash/digest sha256 .escape-html)])
<Alice>
true
<b>
#hash #sha256 2d140a5655b694b51aecece118441a80fdd245852dc87ae6babb33de709aba34
[(strings/escape)
(strings/unescape html-text a b)
((strings/escape html-text))
((strings/unescape html-text) 1)]
(strings/escape)
(strings/unescape html-text a b)
((strings/escape html-text))
((strings/unescape html-text) 1)
#err <arity mismatch>
#err <arity mismatch>
#err <arity mismatch>
#err <mismatch type>
9.1.17. strings/unescape ↑
Function(strings/unescape mode text) → string
(strings/unescape mode) → function
Decodes text for the same modes as strings/escape. With only a mode, returns
a unary decoding function that prints and hashes as its call, such as
(strings/unescape url-query-value). Invalid JSON Pointer, URL percent, CSV, and quoted
string escapes produce #err <invalid value>, as do URL percent escapes whose
bytes are not UTF-8, since strings are always Unicode text. HTML decoding recognizes
semicolon-terminated named and numeric character references.
(strings/unescape json-pointer a~1b~0) ; => a/b~
(strings/unescape html-text "<Alice>") ; => "<Alice>"
(let [decode (strings/unescape url-query-value)]
(.decode cats%20%26%20dogs)) ; => "cats & dogs"
9.1.18. strings/normalize ↑
Function(strings/normalize s) → string
(strings/normalize s form) → string
Returns s normalized to the Unicode normalization form, defaulting to NFC.
The form is one of the exact strings NFC, NFD, NFKC, or NFKD.
Other strings produce an error.
NFC and NFD apply canonical normalization.
NFKC and NFKD also apply compatibility normalization, which can remove meaningful distinctions.
Normalization does not perform case folding or guarantee visually distinct identifiers.
[(strings/normalize "é") (strings/normalize "é" NFD) (strings/normalize "A①ffi" NFKC) (strings/normalize "Å①" NFKD) (strings/normalize "①" NFC) (strings/normalize "가" NFC) (strings/normalize "" NFD) (strings/normalize "É" NFC) (strings/normalize "ࢡ" NFC)]
(strings/normalize é) (strings/normalize é NFD) (strings/normalize A①ffi NFKC) (strings/normalize Å① NFKD) (strings/normalize ① NFC) (strings/normalize 가 NFC) (strings/normalize "" NFD) (strings/normalize É NFC) (strings/normalize ࢡ NFC)
é é A1ffi Å1 ① 가 "" É ࢡ
(strings/normalize "é" nfc)
(strings/normalize é nfc)
#err <form invalid normalization>
9.1.19. strings/from-code-points ↑
Function(strings/from-code-points points) → string
Returns the string formed by the Unicode scalar values in the list points, in order.
Each element must be an integer from zero through 1114111, excluding the surrogate range 55296 through 57343. Invalid scalar values and non-integer elements produce errors. An empty list produces the empty string. Unassigned code points and noncharacters are accepted when they are valid scalar values. The result is not normalized.
[(strings/from-code-points [65 769]) (strings/from-code-points [128578]) (strings/from-code-points []) (strings/from-code-points [0 65]) (strings/from-code-points [1114111]) (strings/from-code-points [888])]
(strings/from-code-points [65 769]) (strings/from-code-points [128578]) (strings/from-code-points []) (strings/from-code-points [0 65]) (strings/from-code-points [1114111]) (strings/from-code-points [888])
Á 🙂 "" "\u0000A"
9.2. strings/humanize ↑
Function(strings/humanize value options?) → string
Formats an integer, float, duration, or UTC value as readable text. Options are a map with string keys. Unknown options and options irrelevant to the value's kind are errors.
| Value kind | Options | Defaults |
|---|---|---|
| Integer or float | scale, unit, precision |
SI scale, empty unit, precision 1 |
| Duration | precision |
Precision 1 |
| UTC | mode, now |
Absolute mode |
For numbers, scale is si or binary. SI uses powers of 1000 with prefixes
such as k and M; binary uses powers of 1024 with prefixes such as Ki and
Mi. A string unit is appended directly, allowing B, /s, and Unicode
units. Signed integers retain full precision before rounding.
precision is an integer from zero through nine and selects the maximum
fractional decimal places. Rounding is halfway away from zero; trailing
fractional zeros are omitted. A rounded value can carry into the next unit:
999950 at the default precision becomes 1M.
(strings/humanize 1500) (strings/humanize 1536 {scale binary unit B}) (strings/humanize 1234 {precision 2 unit "/s"}) (strings/humanize -1250) (strings/humanize 999950) (strings/humanize 9223372036854775807 {precision 9})
1.5k 1.5KiB 1.23k/s -1.3k 1M 9.223372037E
Durations use hours, minutes, seconds, or a suitable subsecond unit. Examples
include 1h 30m, 2.5s, 150µs, and 100ns. The precision applies to the
displayed fractional unit; the stored tick value is unchanged.
(strings/humanize #dur PT1H30M) (strings/humanize #dur PT2.5S) (strings/humanize #dur PT0.00015S) (strings/humanize #dur PT59.99S) (strings/humanize #dur PT1.2345678S {precision 9}) (strings/humanize #dur PT0.0000001S)
"1h 30m" 2.5s 150µs 1m 1.2345678s 100ns
UTC mode is absolute or relative. Absolute formatting produces the
canonical UTC timestamp, retaining its 100 ns precision. Relative formatting
requires an explicit UTC now option and produces text such as 3 minutes ago
or in 2 days. It selects the largest whole unit among days, hours, minutes,
and seconds; differences shorter than a second produce now. Supplying now
in absolute mode is an error. The function never reads the clock implicitly.
(strings/humanize #utc 2026-10-29T12:00:00.1234567Z) (strings/humanize #utc 2026-10-29T12:00Z {mode relative now #utc 2026-10-29T12:03Z}) (strings/humanize #utc 2026-10-31T12:00Z {mode relative now #utc 2026-10-29T12:00Z}) (strings/humanize #utc 2026-10-29T12:00Z {mode relative now #utc 2026-10-29T12:00Z})
(strings/humanize #utc 2026-10-29T12:00:00.1234567Z) (strings/humanize #utc 2026-10-29T12:00:00Z {mode relative now #utc 2026-10-29T12:03:00Z}) (strings/humanize #utc 2026-10-31T12:00:00Z {mode relative now #utc 2026-10-29T12:00:00Z}) (strings/humanize #utc 2026-10-29T12:00:00Z {mode relative now #utc 2026-10-29T12:00:00Z})
2026-10-29T12:00:00.1234567Z "3 minutes ago" "in 2 days" now
(strings/humanize #utc 2026-10-29T12:00Z {mode relative})
(strings/humanize #utc 2026-10-29T12:00:00Z {mode relative})
#err <invalid value>
See the Time library for temporal constructors, arithmetic,
calendar operations, and time/now.
10. Regular Expression Functions ↑
The regex/ namespace uses regular expressions with leftmost-first matching.
Patterns support literal text, character classes, Unicode-aware shorthand classes,
alternatives, numbered and named captures, anchors, and greedy or lazy repetition.
Backreferences and lookaround are not supported; PEG provides capture-dependent
matching and lookahead for grammar-oriented parsing. Matching uses a non-backtracking
algorithm with work proportional to the input length for a fixed pattern.
Choice and capture priority follow Go/RE2-style leftmost-first matching, including
when a repeated group can match empty text.
A #re literal contains a pattern string and has a distinct regex runtime type.
Evaluating it compiles the pattern; an invalid pattern produces an error.
Quoting a literal preserves source data without compiling it. Regex values retain
their original pattern text for formatting and compare by that text.
Matching uses UTF-8 and Unicode properties for shorthand classes by default.
Unicode category escapes \p{...} and \P{...} support L, Lu, Ll,
N, Nd, Mn, Pc, Z, and Zs, and their long names.
Leading flag groups enable Unicode simple case folding ((?i)), multiline
anchors ((?m)), and matching newlines with dot ((?s)). Flags may be
combined, as in (?im). Scoped flag groups are not supported.
The \C byte-matching construct is rejected because captures must not split a UTF-8 code point.
re denotes a compiled pattern. Literal text operations use strings/.
Functions that return matches accept an optional final options map, tagged
with named, indices, or both. The named tag selects a map of named
captures, with {} when the pattern has no named groups. The indices tag
returns zero-based, start-inclusive, end-exclusive code-point ranges instead of
text. With indices and no capture groups, the result is a single range; with
groups, it is a list of ranges. Combine the tags to map names to ranges. An
unmatched capture is null. Without options, regex/groups defines the result
format.
(regex/find #re "(?<year>[0-9]{4})" "2026" <named>)
; {year "2026"}
(regex/find #re "(?<year>[0-9]{4})" "2026" <named indices>)
; {year [0 4]}
10.1. Regex functions ↑
10.1.1. regex/pattern ↑
Function(regex/pattern s) → pattern
Returns a compiled pattern for string s, or s unchanged if it is already a pattern.
Invalid patterns produce an error.
10.1.2. regex/matcher ↑
Function(regex/matcher re s) → matcher
Returns a new mutable matcher for re in string s, for use with regex/find and regex/groups.
A matcher is an opaque value owned by its runtime. It can be passed between
evaluations in that runtime, but it cannot be serialized or used in another runtime.
The current match is cleared when a search reaches the end without a match.
10.1.3. regex/find ↑
Function(regex/find matcher [opts]) → string, list, map, or null
(regex/find re s [opts]) → string, list, map, or null
Returns the next match from matcher, or the first match of re in string s.
Returns null if there is no match.
The matcher form advances matcher.
The pattern and string form starts a fresh search.
Results use the capture format of regex/groups.
10.1.4. regex/groups ↑
Function(regex/groups matcher [opts]) → string, list, or map
Returns the current match from matcher without advancing it.
Without options and with no capture groups, returns the matched string.
Otherwise, returns a list containing the full match followed by each capture in group-number order.
An unmatched group is null.
Without a current match, produces an error.
10.1.5. regex/matches ↑
Function(regex/matches re s [opts]) → string, list, map, or null
Returns the match if re matches the entire string s, otherwise null.
Results use the capture format of regex/groups.
10.1.6. regex/find-all ↑
Function(regex/find-all re s [opts]) → list
Returns an eager list of successive, non-overlapping matches of re in string s.
Each item uses the capture format of regex/groups.
Returns an empty list if there are no matches.
After an empty match, the search first tries a nonempty match at the same
position; if there is none, it advances by one code point.
10.1.7. regex/split ↑
Function(regex/split s re) → list
(regex/split s re limit) → list
Returns a list of parts of string s split on matches of re.
The integer limit defaults to zero.
A positive limit permits at most that many parts, with the unsplit suffix in the last part.
Zero omits trailing empty strings.
A negative limit preserves them.
A zero-width match at the start of s does not create a leading empty part.
If no split occurs, the result contains s as its only part.
10.1.8. regex/replace ↑
Function(regex/replace s re replacement [opts]) → string
Returns string s with all non-overlapping matches of re replaced by replacement.
replacement is a string or a function of the match.
In a replacement string, $1, $2, etc. substitute captured groups.
$0 substitutes the whole match. A backslash escapes the next character; an
unmatched capture substitutes an empty string. When a match is replaced, an invalid group number or a trailing backslash
produces an error. When digits follow a group number, the
longest valid group number is used. Use regex/quote-replacement for literal
replacement text.
A function receives each match in regex/groups format, in search order, and must return a string, which is inserted literally.
The optional result options select the argument format for a function replacement.
Result options are invalid for a string replacement.
10.1.9. regex/replace-first ↑
Function(regex/replace-first s re replacement [opts]) → string
Returns string s with the first match of re replaced by replacement.
Uses the replacement rules of regex/replace.
10.1.10. regex/quote-replacement ↑
Function(regex/quote-replacement replacement) → string
Returns string replacement escaped for literal use with regex/replace or regex/replace-first.
11. Mathematics Functions ↑
11.1. Math functions ↑
The math/ functions accept numbers, and selected functions also accept temporal values. Arguments to a function must all have
the same numeric type; mixed integers and floats produce #err <mismatch type>.
math/abs also accepts a duration. Each function describes its result and
errors below. The namespace includes constants for pi, tau, e, and conversion
between degrees and radians.
11.1.1. math/pi ↑
The constant :math/pi is the float 3.141592653589793, the nearest binary64 approximation to pi.
11.1.2. math/tau ↑
The constant :math/tau is the float 6.283185307179586, the nearest binary64 approximation to two times pi.
11.1.3. math/deg2rad ↑
The constant :math/deg2rad is pi divided by 180, as a float. Multiply degrees
by this value to convert them to radians.
11.1.4. math/rad2deg ↑
The constant :math/rad2deg is 180 divided by pi, as a float. Multiply radians
by this value to convert them to degrees.
11.1.5. math/e ↑
The constant :math/e is the float 2.718281828459045, the nearest binary64 approximation to the base of natural logarithms.
11.1.6. math/floor ↑
Function(math/floor n) → float
Returns the greatest integral-valued float less than or equal to n after conversion to a float.
[(math/floor 1.8) (math/floor -1.2) (math/floor 2) (math/floor 0.0) (math/floor 9007199254740993) (math/floor 100000000000000000000.0)]
(math/floor 1.8) (math/floor -1.2) (math/floor 2) (math/floor 0.0) (math/floor 9007199254740993) (math/floor 100000000000000000000.0)
1.0 -2.0 2.0 0.0 9007199254740992.0 100000000000000000000.0
(math/ceil n) → float
Returns the least integral-valued float greater than or equal to n after conversion to a float.
[(math/ceil 1.2) (math/ceil -1.8) (math/ceil 2) (math/ceil 0.0) (math/ceil -0.2) (math/ceil 9007199254740993) (math/ceil 100000000000000000000.0)]
(math/ceil 1.2) (math/ceil -1.8) (math/ceil 2) (math/ceil 0.0) (math/ceil -0.2) (math/ceil 9007199254740993) (math/ceil 100000000000000000000.0)
2.0 -1.0 2.0 0.0 0.0 9007199254740992.0 100000000000000000000.0
11.1.8. math/round ↑
Function(math/round n) → integer
Returns the integer nearest to n, rounding halfway cases away from zero.
Integer inputs are returned unchanged.
A float whose rounded value is outside the signed 64-bit integer range produces #err <integer overflow>.
[(math/round 1.4) (math/round 1.5) (math/round 2.5) (math/round -1.5) (math/round -2.5) (math/round -1.6) (math/round 0.5) (math/round -0.5) (math/round 0.49999999999999994) (math/round -0.5000000000000001) (math/round 7) (math/round 9007199254740993) (math/round 9223372036854775807) (math/round -9223372036854775808) (math/round -9223372036854776000.0) (math/round 9223372036854775000.0)]
(math/round 1.4) (math/round 1.5) (math/round 2.5) (math/round -1.5) (math/round -2.5) (math/round -1.6) (math/round 0.5) (math/round -0.5) (math/round 0.49999999999999994) (math/round -0.5000000000000001) (math/round 7) (math/round 9007199254740993) (math/round 9223372036854775807) (math/round -9223372036854775808) (math/round -9223372036854776000.0) (math/round 9223372036854775000.0)
1 2 3 -2 -3 -2 1 -1 0 -1 7 9007199254740993 9223372036854775807 -9223372036854775808 -9223372036854775808 9223372036854774784
(math/rint n) → float
Returns the integral-valued float nearest to n after conversion to a float, choosing the even integer in halfway cases.
The result does not depend on the host's current rounding mode.
[(math/rint 1.5) (math/rint 2.5) (math/rint -1.5) (math/rint -2.5) (math/rint 0.5) (math/rint -0.5) (math/rint 7) (math/rint 9007199254740993)]
(math/rint 1.5) (math/rint 2.5) (math/rint -1.5) (math/rint -2.5) (math/rint 0.5) (math/rint -0.5) (math/rint 7) (math/rint 9007199254740993)
2.0 2.0 -2.0 -2.0 0.0 0.0 7.0 9007199254740992.0
11.1.10. math/trunc ↑
Function(math/trunc n) → float
Returns n converted to a float with its fractional part discarded, rounding toward zero.
[(math/trunc 1.8) (math/trunc -1.8) (math/trunc 2) (math/trunc -0.2) (math/trunc 9007199254740993)]
(math/trunc 1.8) (math/trunc -1.8) (math/trunc 2) (math/trunc -0.2) (math/trunc 9007199254740993)
1.0 -1.0 2.0 0.0 9007199254740992.0
(math/pow base exponent) → float
Returns base raised to exponent.
A zero exponent returns 1.0, including when base is zero.
A negative base requires an integral-valued exponent after float conversion.
A zero base with a negative exponent produces #err <float overflow>.
[(math/pow 2 3) (math/pow -2 3) (math/pow 9 0.5) (math/pow 0 0) (math/pow 2 -3)]
(math/pow 2 3) (math/pow -2 3) (math/pow 9 0.5) (math/pow 0 0) (math/pow 2 -3)
8.0 -8.0 #err <mismatch type> 1.0 0.125
(math/sqrt n) → float
Returns the square root of n.
A negative n produces #err <float overflow>.
A zero result has the same sign as n.
[(math/sqrt 0) (math/sqrt 4) (math/sqrt 9.0)]
(math/sqrt 0) (math/sqrt 4) (math/sqrt 9.0)
0.0 2.0 3.0
(math/cbrt n) → float
Returns the real cube root of n, with the same sign as n.
[(math/round (* 1000000000000.0 (math/cbrt -8))) (math/round (* 1000000000000.0 (math/cbrt 0))) (math/round (* 1000000000000.0 (math/cbrt 27)))]
(math/round (* 1000000000000.0 (math/cbrt -8))) (math/round (* 1000000000000.0 (math/cbrt 0))) (math/round (* 1000000000000.0 (math/cbrt 27)))
-2000000000000 0 3000000000000
11.1.14. math/hypot ↑
Function(math/hypot x y) → float
Returns the square root of x*x + y*y without intermediate overflow or underflow.
An out-of-range final result produces #err <float overflow>.
[(math/hypot 3 4) (math/hypot -3 -4) (math/hypot 0 0) (math/hypot 0 1.5)]
(math/hypot 3 4) (math/hypot -3 -4) (math/hypot 0 0) (math/hypot 0 1.5)
5.0 5.0 0.0 #err <mismatch type>
[(math/round (* 1000000000000.0 (/ (math/hypot 1e+308 1e+308) 1e+308))) (math/round (* 1000000000000.0 (/ (math/hypot 1e-308 1e-308) 1e-308)))]
(math/round (* 1000000000000.0 (/ (math/hypot 1e+308 1e+308) 1e+308))) (math/round (* 1000000000000.0 (/ (math/hypot 1e-308 1e-308) 1e-308)))
1414213562373 1414213562373
(math/exp n) → float
Returns e raised to n.
[(math/round (* 1000000000000.0 (math/exp -1))) (math/round (* 1000000000000.0 (math/exp 0))) (math/round (* 1000000000000.0 (math/exp 1)))]
(math/round (* 1000000000000.0 (math/exp -1))) (math/round (* 1000000000000.0 (math/exp 0))) (math/round (* 1000000000000.0 (math/exp 1)))
367879441171 1000000000000 2718281828459
(math/exp2 n) → float
Returns two raised to n.
[(math/exp2 -3) (math/exp2 0) (math/exp2 3)]
(math/exp2 -3) (math/exp2 0) (math/exp2 3)
0.125 1.0 8.0
11.1.17. math/expm1 ↑
Function(math/expm1 n) → float
Returns e raised to n minus one, computed without the loss of precision from subtracting one from math/exp's rounded result near zero.
[(math/round (* 1000000000000.0 (math/expm1 -1))) (math/round (* 1000000000000.0 (math/expm1 0))) (math/round (* 1000000000000.0 (math/expm1 1)))]
(math/round (* 1000000000000.0 (math/expm1 -1))) (math/round (* 1000000000000.0 (math/expm1 0))) (math/round (* 1000000000000.0 (math/expm1 1)))
-632120558829 0 1718281828459
(math/log n) → float
(math/log n base) → float
Returns the natural logarithm of n, or its logarithm in the supplied base.
When base is supplied, both numbers must have the same type. The base must be
positive and cannot be one.
A zero or negative n produces #err <float overflow>.
[(math/round (* 1000000000000.0 (math/log 0.5))) (math/round (* 1000000000000.0 (math/log 1))) (math/round (* 1000000000000.0 (math/log 2)))]
(math/round (* 1000000000000.0 (math/log 0.5))) (math/round (* 1000000000000.0 (math/log 1))) (math/round (* 1000000000000.0 (math/log 2)))
-693147180560 0 693147180560
(math/log2 n) → float
Returns the base-two logarithm of n.
A zero or negative n produces #err <float overflow>.
[(math/log2 0.5) (math/log2 1) (math/log2 2) (math/log2 8)]
(math/log2 0.5) (math/log2 1) (math/log2 2) (math/log2 8)
-1.0 0.0 1.0 3.0
11.1.20. math/log10 ↑
Function(math/log10 n) → float
Returns the base-ten logarithm of n.
A zero or negative n produces #err <float overflow>.
[(math/log10 0.1) (math/log10 1) (math/log10 10) (math/log10 100)]
(math/log10 0.1) (math/log10 1) (math/log10 10) (math/log10 100)
-1.0 0.0 1.0 2.0
11.1.21. math/log1p ↑
Function(math/log1p n) → float
Returns the natural logarithm of one plus n, computed without the loss of precision from rounding 1 + n near zero.
An n less than or equal to -1 produces #err <float overflow>.
[(math/round (* 1000000000000.0 (math/log1p -0.5))) (math/round (* 1000000000000.0 (math/log1p 0))) (math/round (* 1000000000000.0 (math/log1p 1)))]
(math/round (* 1000000000000.0 (math/log1p -0.5))) (math/round (* 1000000000000.0 (math/log1p 0))) (math/round (* 1000000000000.0 (math/log1p 1)))
-693147180560 0 693147180560
(math/sin angle) → float
Returns the sine of angle in radians.
[(math/round (* 1000000000000.0 (math/sin -1))) (math/round (* 1000000000000.0 (math/sin 0))) (math/round (* 1000000000000.0 (math/sin 1)))]
(math/round (* 1000000000000.0 (math/sin -1))) (math/round (* 1000000000000.0 (math/sin 0))) (math/round (* 1000000000000.0 (math/sin 1)))
-841470984808 0 841470984808
(math/cos angle) → float
Returns the cosine of angle in radians.
[(math/round (* 1000000000000.0 (math/cos -1))) (math/round (* 1000000000000.0 (math/cos 0))) (math/round (* 1000000000000.0 (math/cos 1)))]
(math/round (* 1000000000000.0 (math/cos -1))) (math/round (* 1000000000000.0 (math/cos 0))) (math/round (* 1000000000000.0 (math/cos 1)))
540302305868 1000000000000 540302305868
(math/tan angle) → float
Returns the tangent of angle in radians.
[(math/round (* 1000000000000.0 (math/tan -1))) (math/round (* 1000000000000.0 (math/tan 0))) (math/round (* 1000000000000.0 (math/tan 1)))]
(math/round (* 1000000000000.0 (math/tan -1))) (math/round (* 1000000000000.0 (math/tan 0))) (math/round (* 1000000000000.0 (math/tan 1)))
-1557407724655 0 1557407724655
(math/asin n) → float
Returns the arc sine of n in radians, in the range from negative pi/2 to pi/2.
An n outside [-1, 1] produces #err <float overflow>.
[(math/round (* 1000000000000.0 (math/asin -1))) (math/round (* 1000000000000.0 (math/asin 0))) (math/round (* 1000000000000.0 (math/asin 1)))]
(math/round (* 1000000000000.0 (math/asin -1))) (math/round (* 1000000000000.0 (math/asin 0))) (math/round (* 1000000000000.0 (math/asin 1)))
-1570796326795 0 1570796326795
(math/acos n) → float
Returns the arc cosine of n in radians, in the range from zero to pi.
An n outside [-1, 1] produces #err <float overflow>.
[(math/round (* 1000000000000.0 (math/acos -1))) (math/round (* 1000000000000.0 (math/acos 0))) (math/round (* 1000000000000.0 (math/acos 1)))]
(math/round (* 1000000000000.0 (math/acos -1))) (math/round (* 1000000000000.0 (math/acos 0))) (math/round (* 1000000000000.0 (math/acos 1)))
3141592653590 1570796326795 0
(math/atan n) → float
Returns the arc tangent of n in radians, in the range from negative pi/2 to pi/2.
[(math/round (* 1000000000000.0 (math/atan -1))) (math/round (* 1000000000000.0 (math/atan 0))) (math/round (* 1000000000000.0 (math/atan 1)))]
(math/round (* 1000000000000.0 (math/atan -1))) (math/round (* 1000000000000.0 (math/atan 0))) (math/round (* 1000000000000.0 (math/atan 1)))
-785398163397 0 785398163397
11.1.28. math/atan2 ↑
Function(math/atan2 y x) → float
Returns the angle of the point (x, y) in radians, in the range from negative pi to pi, using both signs to select the quadrant.
The y argument comes first.
For zero y and positive x or positive zero x, returns zero with y's sign.
For zero y and negative x or negative zero x, returns pi with y's sign.
[(math/round (* 1000000000000.0 (math/atan2 0 1))) (math/round (* 1000000000000.0 (math/atan2 1 1))) (math/round (* 1000000000000.0 (math/atan2 1 -1))) (math/round (* 1000000000000.0 (math/atan2 -1 -1))) (math/round (* 1000000000000.0 (math/atan2 -1 1)))]
(math/round (* 1000000000000.0 (math/atan2 0 1))) (math/round (* 1000000000000.0 (math/atan2 1 1))) (math/round (* 1000000000000.0 (math/atan2 1 -1))) (math/round (* 1000000000000.0 (math/atan2 -1 -1))) (math/round (* 1000000000000.0 (math/atan2 -1 1)))
0 785398163397 2356194490192 -2356194490192 -785398163397
[(math/round (* 1000000000000.0 (math/atan2 0.0 (- 0.0)))) (math/round (* 1000000000000.0 (math/atan2 (- 0.0) (- 0.0)))) (math/copy-sign 1.0 (math/atan2 (- 0.0) 0.0))]
(math/round (* 1000000000000.0 (math/atan2 0.0 (- 0.0)))) (math/round (* 1000000000000.0 (math/atan2 (- 0.0) (- 0.0)))) (math/copy-sign 1.0 (math/atan2 (- 0.0) 0.0))
3141592653590 -3141592653590 -1.0
(math/sinh n) → float
Returns the hyperbolic sine of n.
[(math/round (* 1000000000000.0 (math/sinh -1))) (math/round (* 1000000000000.0 (math/sinh 0))) (math/round (* 1000000000000.0 (math/sinh 1)))]
(math/round (* 1000000000000.0 (math/sinh -1))) (math/round (* 1000000000000.0 (math/sinh 0))) (math/round (* 1000000000000.0 (math/sinh 1)))
-1175201193644 0 1175201193644
(math/cosh n) → float
Returns the hyperbolic cosine of n.
[(math/round (* 1000000000000.0 (math/cosh -1))) (math/round (* 1000000000000.0 (math/cosh 0))) (math/round (* 1000000000000.0 (math/cosh 1)))]
(math/round (* 1000000000000.0 (math/cosh -1))) (math/round (* 1000000000000.0 (math/cosh 0))) (math/round (* 1000000000000.0 (math/cosh 1)))
1543080634815 1000000000000 1543080634815
(math/tanh n) → float
Returns the hyperbolic tangent of n.
[(math/round (* 1000000000000.0 (math/tanh -1))) (math/round (* 1000000000000.0 (math/tanh 0))) (math/round (* 1000000000000.0 (math/tanh 1)))]
(math/round (* 1000000000000.0 (math/tanh -1))) (math/round (* 1000000000000.0 (math/tanh 0))) (math/round (* 1000000000000.0 (math/tanh 1)))
-761594155956 0 761594155956
11.1.32. math/asinh ↑
Function(math/asinh n) → float
Returns the inverse hyperbolic sine of n.
[(math/round (* 1000000000000.0 (math/asinh -1))) (math/round (* 1000000000000.0 (math/asinh 0))) (math/round (* 1000000000000.0 (math/asinh 1)))]
(math/round (* 1000000000000.0 (math/asinh -1))) (math/round (* 1000000000000.0 (math/asinh 0))) (math/round (* 1000000000000.0 (math/asinh 1)))
-881373587020 0 881373587020
11.1.33. math/acosh ↑
Function(math/acosh n) → float
Returns the nonnegative inverse hyperbolic cosine of n.
An n less than 1 produces #err <float overflow>.
[(math/round (* 1000000000000.0 (math/acosh 1))) (math/round (* 1000000000000.0 (math/acosh 2))) (math/round (* 1000000000000.0 (math/acosh 3)))]
(math/round (* 1000000000000.0 (math/acosh 1))) (math/round (* 1000000000000.0 (math/acosh 2))) (math/round (* 1000000000000.0 (math/acosh 3)))
0 1316957896925 1762747174039
11.1.34. math/atanh ↑
Function(math/atanh n) → float
Returns the inverse hyperbolic tangent of n.
An n outside the open interval from -1 to 1 produces #err <float overflow>.
[(math/round (* 1000000000000.0 (math/atanh -0.5))) (math/round (* 1000000000000.0 (math/atanh 0))) (math/round (* 1000000000000.0 (math/atanh 0.5)))]
(math/round (* 1000000000000.0 (math/atanh -0.5))) (math/round (* 1000000000000.0 (math/atanh 0))) (math/round (* 1000000000000.0 (math/atanh 0.5)))
-549306144334 0 549306144334
11.1.35. math/radians ↑
Function(math/radians degrees) → float
Returns the angle degrees converted to radians.
[(math/round (* 1000000000000.0 (math/radians -90))) (math/round (* 1000000000000.0 (math/radians 0))) (math/round (* 1000000000000.0 (math/radians 180)))]
(math/round (* 1000000000000.0 (math/radians -90))) (math/round (* 1000000000000.0 (math/radians 0))) (math/round (* 1000000000000.0 (math/radians 180)))
-1570796326795 0 3141592653590
11.1.36. math/degrees ↑
Function(math/degrees radians) → float
Returns the angle radians converted to degrees.
[(math/round (* 1000000000000.0 (math/degrees -1))) (math/round (* 1000000000000.0 (math/degrees 0))) (math/round (* 1000000000000.0 (math/degrees 1)))]
(math/round (* 1000000000000.0 (math/degrees -1))) (math/round (* 1000000000000.0 (math/degrees 0))) (math/round (* 1000000000000.0 (math/degrees 1)))
-57295779513082 0 57295779513082
11.1.37. math/signum ↑
Function(math/signum n) → float
Returns -1.0 for negative n, 1.0 for positive n, or zero with n's sign for zero.
[(math/signum -3) (math/signum 0) (math/signum 5.5) (math/signum (- 0.0))]
(math/signum -3) (math/signum 0) (math/signum 5.5) (math/signum (- 0.0))
-1.0 0.0 1.0 0.0
11.1.38. math/copy-sign ↑
Function(math/copy-sign magnitude sign) → float
Returns the absolute value of magnitude with the sign of sign, including the sign of floating-point zero.
[(math/copy-sign 3.5 -2) (math/copy-sign -3.5 2) (math/copy-sign 1.0 (math/copy-sign 0 -1)) (math/copy-sign 2 (- 0.0)) (math/copy-sign -2 0.0)]
(math/copy-sign 3.5 -2) (math/copy-sign -3.5 2) (math/copy-sign 1.0 (math/copy-sign 0 -1)) (math/copy-sign 2 (- 0.0)) (math/copy-sign -2 0.0)
#err <mismatch type> #err <mismatch type> -1.0 #err <mismatch type> #err <mismatch type>
11.1.39. math/floor-div ↑
Function(math/floor-div n d) → integer
Returns the integer quotient of n divided by d, rounded toward negative infinity.
Before division, float arguments are converted to integers by truncating toward zero.
An out-of-range conversion or quotient produces #err <integer overflow>.
A zero divisor after conversion produces #err <division integer zero>.
[(math/floor-div 5 3) (math/floor-div -5 3) (math/floor-div 5 -3) (math/floor-div -5 -3) (math/floor-div 9223372036854775807 1) (math/floor-div -9223372036854775808 1) (math/floor-div 5.9 2.1) (math/floor-div -5.9 2.1)]
(math/floor-div 5 3) (math/floor-div -5 3) (math/floor-div 5 -3) (math/floor-div -5 -3) (math/floor-div 9223372036854775807 1) (math/floor-div -9223372036854775808 1) (math/floor-div 5.9 2.1) (math/floor-div -5.9 2.1)
1 -2 -2 1 9223372036854775807 -9223372036854775808 2 -3
11.1.40. math/floor-mod ↑
Function(math/floor-mod n d) → integer
Returns the integer remainder of n divided by d using a quotient rounded toward negative infinity.
Before division, float arguments are converted to integers by truncating toward zero.
An out-of-range conversion produces #err <integer overflow>.
A zero divisor after conversion produces #err <division integer zero>.
A nonzero result has the divisor's sign and a smaller magnitude.
The quotient need not fit in an integer, so (math/floor-mod -9223372036854775808 -1) returns 0.
[(math/floor-mod 5 3) (math/floor-mod -5 3) (math/floor-mod 5 -3) (math/floor-mod -5 -3) (math/floor-mod -9223372036854775808 -1) (math/floor-mod 9223372036854775807 2) (math/floor-mod 5.9 2.1) (math/floor-mod -5.9 2.1)]
(math/floor-mod 5 3) (math/floor-mod -5 3) (math/floor-mod 5 -3) (math/floor-mod -5 -3) (math/floor-mod -9223372036854775808 -1) (math/floor-mod 9223372036854775807 2) (math/floor-mod 5.9 2.1) (math/floor-mod -5.9 2.1)
2 1 -1 -2 0 1 1 1
(math/abs value) → value
Returns the absolute value, preserving integer or float kind. The absolute value of the minimum integer overflows and is an error. Durations are also accepted and return a duration; the minimum tick duration likewise overflows. UTC timestamps are rejected.
(math/sign number) → integer
Returns integer -1, 0, or 1 according to the argument's sign.
(math/lerp start end fraction) → float
Returns start + (end - start) * fraction as a float. The fraction is not
clamped, so values outside zero through one extrapolate beyond the endpoints.
(math/lerp 10.0 20.0 0.5) (math/lerp 10.0 20.0 1.5) (math/lerp 10 20 0)
15.0 25.0 10.0
11.1.44. math/inverse-lerp ↑
Function(math/inverse-lerp start end value) → float
Returns the fraction locating value between start and end, without
clamping. Equal endpoints are an error.
(math/inverse-lerp 10 20 15) (math/inverse-lerp 10 20 25) (math/inverse-lerp 10 20 10)
0.5 1.5 0.0
11.1.45. math/remap ↑
Function(math/remap input-start input-end output-start output-end value) → float
Maps value from the input interval to the output interval, without clamping.
Equal input endpoints are an error. Reversed intervals are accepted.
(math/remap 0 10 100 200 5) (math/remap 0 10 100 200 15) (math/remap 10 0 100 200 5)
150.0 250.0 150.0
11.1.46. math/zero? ↑
Function(math/zero? n) → boolean
Returns true if n is zero, otherwise false.
(math/zero? 0) (math/zero? 0.0) (math/zero? 3) (math/zero? -2) (math/zero? 0.5) (math/zero? -0.5) (math/zero? -9223372036854775808) (math/zero? 9223372036854775807)
true true false false false false false false
{boolean (math/zero? false) extra (math/zero? 0 0) list (math/zero? []) missing (math/zero?) null-input (math/zero? null) string (math/zero? "0")}
#cases [{output #err <mismatch type> select string} {output #err <mismatch type> select null-input} {output #err <mismatch type> select boolean} {output #err <mismatch type> select list} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select extra}]
(math/odd? n) → boolean
Returns true if integer n is odd, otherwise false.
(math/odd? 1) (math/odd? 2) (math/odd? -3) (math/odd? 0) (math/odd? -2) (math/odd? -9223372036854775808) (math/odd? 9223372036854775807)
true false true false false false true
{boolean (math/odd? true) extra (math/odd? 1 3) float (math/odd? 1.0) fractional (math/odd? 1.5) missing (math/odd?) null-input (math/odd? null) string (math/odd? "1")}
#cases [{output #err <mismatch type> select float} {output #err <mismatch type> select fractional} {output #err <mismatch type> select string} {output #err <mismatch type> select null-input} {output #err <mismatch type> select boolean} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select extra}]
11.1.48. math/even? ↑
Function(math/even? n) → boolean
Returns true if integer n is even, otherwise false.
(math/even? 0) (math/even? 2) (math/even? -3) (math/even? 1) (math/even? -2) (math/even? -9223372036854775808) (math/even? 9223372036854775807)
true true false false true true false
{boolean (math/even? false) extra (math/even? 2 4) float (math/even? 2.0) fractional (math/even? 2.5) missing (math/even?) null-input (math/even? null) string (math/even? "2")}
#cases [{output #err <mismatch type> select float} {output #err <mismatch type> select fractional} {output #err <mismatch type> select string} {output #err <mismatch type> select null-input} {output #err <mismatch type> select boolean} {output #err <arity mismatch> select missing} {output #err <arity mismatch> select extra}]
(math/min value more*) → value
Returns the least argument. Requires at least one argument, and all arguments must have the same numeric or temporal kind. Integer and temporal comparisons retain full 64-bit precision.
(math/min 3 1 2) (math/min 1.5 0.5) (math/min 9007199254740994 9007199254740993)
1 0.5 9007199254740993
(math/max value more*) → value
Returns the greatest argument. Requires at least one argument, and all arguments must have the same numeric or temporal kind. Integer and temporal comparisons retain full 64-bit precision.
(math/max 3 1 2) (math/max 1.5 0.5) (math/max -9007199254740994 -9007199254740993)
3 1.5 -9007199254740993
11.1.51. math/clamp ↑
Function(math/clamp value low high) → value
Returns value limited to the inclusive interval from low through high.
All three arguments must have the same numeric or temporal kind, and low
must not exceed high.
12. Time Functions ↑
The Time library provides typed UTC timestamps and durations, stored as signed
64-bit counts of 100-nanosecond ticks. There are 10000000 ticks per second.
A UTC value counts ticks since 1970-01-01T00:00:00Z, without time-zone or
leap-second state; a duration counts elapsed ticks. These are distinct types
from integers and from each other. No implicit conversion turns a number into a duration.
See DUST for language rules, the core arithmetic reference for shared operators, and the Function Index for all callable entries.
12.1. Range and resolution ↑
One tick is 100 ns, or 0.0000001 s. The signed 64-bit range provides roughly
29,227.7 Gregorian years on either side of the Unix epoch, approximately
27,258 BCE through 31,197 CE. Exact endpoint spellings use astronomical year
numbering, including year zero:
| Payload | Canonical UTC |
|---|---|
-9223372036854775808 ticks |
-27258-04-19T21:11:54.5224192Z |
9223372036854775807 ticks |
31197-09-14T02:48:05.4775807Z |
The range covers recorded history, back to the
first writing in Uruk around
3400 BCE: clay tablets logging deliveries, rations, and labor, the first event logs.
It also leaves room for the prototype's fictional touchstones: Dune's
Butlerian Jihad
and the Horus Heresy, whose
official timeline includes events in 005.M31 through 014.M31, around
30,005–30,014 CE. The Dune Encyclopedia dates its Jihad as 200–108 B.G.; a
conversion to a specific Gregorian year is not part of this API.
The resolution remains fine enough to describe fast motion. Using the
prototype's approximate Parker Solar Probe speed of 191.2 km/s, a 100 ns tick
corresponds to 191200 × 0.0000001 = 0.01912 m, about 19 mm.
NASA reports its record flyby speed as about 430,000 mph,
which gives the same approximately 19 mm scale per tick.
Windows FILETIME
also uses 100 ns units, with a different epoch. A representation's resolution
does not guarantee that a clock or scheduler measures time that accurately.
Canonical timestamps and durations use at most seven fractional second digits. Text accepts up to nine fractional second digits and is rounded to the nearest tick, with halfway cases rounded away from zero. A nonzero timestamp or duration that would round to zero is represented by one tick in magnitude.
(time/utc -9223372036854775808) (time/utc 9223372036854775807) (time/utc 30005-01-01T00:00:00Z)
#utc -27258-04-19T21:11:54.5224192Z #utc 31197-09-14T02:48:05.4775807Z #utc 30005-01-01T00:00:00Z
(time/duration -9223372036854775808) (time/duration 9223372036854775807) (time/units ns (time/duration 1))
#dur -P10675199DT2H48M5.4775808S #dur P10675199DT2H48M5.4775807S 100
#utc 2024-02-29T12:34:56.123456700Z #utc 2024-02-29T12:34:56.12345670Z
#utc 2024-02-29T12:34:56.1234567Z #utc 2024-02-29T12:34:56.1234567Z
#utc 2024-02-29T12:34:56.1234567Z #utc 2024-02-29T12:34:56.1234567Z
#dur PT0.000000100S #dur PT0.00000010S
#dur PT0.0000001S #dur PT0.0000001S
#dur PT0.0000001S #dur PT0.0000001S
#utc 1970-01-01T00:00:00.000000001Z
#utc 1970-01-01T00:00:00.0000001Z
#utc 1970-01-01T00:00:00.0000001Z
12.2. Types and conversion ↑
(time/utc value) → UTC
An integer supplies a raw count of 100 ns ticks since the Unix epoch. A string
supplies a UTC timestamp ending in Z. A float supplies a Julian day, following SQLite's
Julian-day convention: day 0.0 is -4713-11-24T12:00:00Z, 2440587.5 is the Unix epoch,
and 2451545.0 is 2000-01-01T12:00:00Z.
Integer and float arguments deliberately have different units. Use an integer for exact tick counts; Julian-day floats have ordinary floating-point precision limits. Julian days are rounded to the nearest tick, with exact halfway cases rounded away from the Unix epoch. Invalid or out-of-range timestamp input is an error.
(time/utc 0) (time/utc -1) (time/utc 2024-02-29T12:34:56.1234567Z)
#utc 1970-01-01T00:00:00Z #utc 1969-12-31T23:59:59.9999999Z #utc 2024-02-29T12:34:56.1234567Z
(time/utc 0.0) (time/utc 2440587.5) (time/utc 2451545.0)
#utc -4713-11-24T12:00:00Z #utc 1970-01-01T00:00:00Z #utc 2000-01-01T12:00:00Z
12.2.2. time/duration ↑
Function(time/duration value) → duration
(time/duration amount unit) → duration
With one argument, a number supplies a raw 100 ns tick count and a string
supplies an ISO duration. A float is rounded to the nearest tick, with halfway
cases rounded away from zero and a minimum of one tick in magnitude for
nonzero values: (time/duration 1.5) is two ticks.
With two arguments, amount is an integer or float and unit is one of
ns, us, µs, ms, s, m, h, d, w, tick, or ticks.
Both tick spellings mean 100 ns. Days and weeks mean fixed elapsed intervals
of 24 hours and seven days. The result is always a typed duration.
Numeric amounts are rounded to the nearest tick, with halfway cases rounded
away from zero. A nonzero amount that rounds to zero is raised to one tick
in magnitude. For example, (time/duration 1 ns) is one tick,
(time/duration 101 ns) is one tick, and (time/duration -1 ns) is minus
one tick. (time/duration 1.5 ticks) is two ticks and
(time/duration 1.5 h) returns #dur PT1H30M.
Years and months are calendar offsets, not fixed duration
units; use time/shift for them.
(time/duration 0) (time/duration -1) (time/duration PT1H30M)
#dur PT0S #dur -PT0.0000001S #dur PT1H30M
(time/duration 100 ns) (time/duration 1 us) (time/duration 1 µs) (time/duration 1 ms) (time/duration 1 s) (time/duration 1 m) (time/duration 1 h) (time/duration 1 d) (time/duration 1 w) (time/duration 1 tick) (time/duration 1 ticks) (time/duration 1.5 h)
#dur PT0.0000001S #dur PT0.000001S #dur PT0.000001S #dur PT0.001S #dur PT1S #dur PT1M #dur PT1H #dur P1D #dur P7D #dur PT0.0000001S #dur PT0.0000001S #dur PT1H30M
(time/duration 1.5) (time/duration -1.5) (time/duration 1.4)
#dur PT0.0000002S #dur -PT0.0000002S #dur PT0.0000001S
12.2.3. time/units ↑
Function(time/units unit value) → number
Expresses a UTC timestamp or duration in the requested unit. The unit argument comes first. For UTC, the result measures time since the Unix epoch; for a duration, it measures the elapsed interval. The underlying value remains a signed count of 100 ns ticks.
| Unit | Meaning | Result kind |
|---|---|---|
tick, ticks |
100 ns ticks | Integer |
ns |
Nanoseconds | Integer |
us, µs |
Microseconds | Float |
ms |
Milliseconds | Float |
s |
Seconds | Float |
m |
Minutes | Float |
h |
Hours | Float |
d |
24-hour days | Float |
w |
Seven-day weeks | Float |
Float results retain fractional units. For example,
(time/units s #dur PT1.5S) returns 1.5, while
(time/units ns (time/duration 1)) returns integer 100.
Float conversions can lose precision for large values.
Exact tick counts retain the full signed 64-bit range:
(time/units ticks (time/utc 1)) (time/units ticks (time/duration 1 ticks)) (time/units ticks (time/duration -9223372036854775808))
1 1 -9223372036854775808
Nanosecond results are also integers:
(time/units ns #utc 1970-01-01T00:00:00Z) (time/units ns #dur PT1.5S) (time/units ns #utc 1969-12-31T23:59:59.9999999Z)
0 1500000000 -100
Larger units retain fractional duration values:
(time/units us (time/duration 5)) (time/units us (time/duration -5)) (time/units µs (time/duration 5)) (time/units µs (time/duration -5)) (time/units ms (time/duration 2500)) (time/units ms (time/duration -2500)) (time/units s (time/duration 1250000)) (time/units s (time/duration -1250000)) (time/units m (time/duration 75000000)) (time/units m (time/duration -75000000)) (time/units h (time/duration 4500000000)) (time/units h (time/duration -4500000000)) (time/units d (time/duration 108000000000)) (time/units d (time/duration -108000000000)) (time/units w (time/duration 756000000000)) (time/units w (time/duration -756000000000))
0.5 -0.5 0.5 -0.5 0.25 -0.25 0.125 -0.125 0.125 -0.125 0.125 -0.125 0.125 -0.125 0.125 -0.125
UTC conversions measure fractional units from the Unix epoch:
(time/units us (time/utc 5)) (time/units us (time/utc -5)) (time/units µs (time/utc 5)) (time/units µs (time/utc -5)) (time/units ms (time/utc 2500)) (time/units ms (time/utc -2500)) (time/units s (time/utc 1250000)) (time/units s (time/utc -1250000)) (time/units m (time/utc 75000000)) (time/units m (time/utc -75000000)) (time/units h (time/utc 4500000000)) (time/units h (time/utc -4500000000)) (time/units d (time/utc 108000000000)) (time/units d (time/utc -108000000000)) (time/units w (time/utc 756000000000)) (time/units w (time/utc -756000000000))
0.5 -0.5 0.5 -0.5 0.25 -0.25 0.125 -0.125 0.125 -0.125 0.125 -0.125 0.125 -0.125 0.125 -0.125
Conversion to a finer unit can exceed the signed 64-bit range. In particular, expressing ticks as nanoseconds requires multiplication by 100, so a valid timestamp or duration can overflow that conversion. Overflow is checked.
See the time/units conformance cases.
(time/utc? value) → boolean
Returns whether the value has the UTC kind. Durations and integers return false.
(time/utc? #utc 1970-01-01T00:00Z) (time/utc? #dur PT0S) (time/utc? 0)
(time/utc? #utc 1970-01-01T00:00:00Z) (time/utc? #dur PT0S) (time/utc? 0)
true false false
12.2.5. time/duration? ↑
Function(time/duration? value) → boolean
Returns whether the value has the duration kind. UTC timestamps and integers
return false.
(time/duration? #dur PT0S) (time/duration? #utc 1970-01-01T00:00Z) (time/duration? 0)
(time/duration? #dur PT0S) (time/duration? #utc 1970-01-01T00:00:00Z) (time/duration? 0)
true false false
12.2.6. time/format ↑
Function(time/format value) → string
Formats a UTC timestamp or duration as its canonical ISO string, without a
DUST tag prefix. The full 100 ns tick precision is retained. For readable display text,
use the String library's strings/humanize function.
(time/format #utc 1970-01-01T00:00:00Z) (time/format #utc 1969-12-31T23:59:59.9999999Z) (time/format #dur PT1.5S)
1970-01-01T00:00:00Z 1969-12-31T23:59:59.9999999Z PT1.5S
12.3. Temporal arithmetic ↑
The Core library's shared operators accept these explicit temporal combinations. Each result retains its indicated kind. Arithmetic uses exact signed 64-bit ticks and reports overflow rather than wrapping.
| Operation | Result |
|---|---|
| UTC + duration; duration + UTC | UTC |
| Duration + duration | Duration |
| UTC - duration | UTC |
| UTC - UTC; duration - duration | Duration |
| Duration * integer; integer * duration | Duration |
| Duration / integer | Duration, ticks truncated toward zero |
| Duration / duration | Float ratio |
Operations with several operands fold from left to right, so the kind of each intermediate result determines the next operation. Unary addition preserves UTC and duration values. Unary subtraction negates a duration; a timestamp cannot be negated. Unary multiplication preserves a duration, while a duration has no unary reciprocal.
(+ #utc 1970-01-01T00:00:00Z #dur PT1S) (+ #dur PT1S #utc 1970-01-01T00:00:00Z) (+ #dur PT1S #dur PT2S) (+ #utc 1970-01-01T00:00:00Z) (+ #dur PT1S)
#utc 1970-01-01T00:00:01Z #utc 1970-01-01T00:00:01Z #dur PT3S #utc 1970-01-01T00:00:00Z #dur PT1S
(- #utc 1970-01-01T00:00:01Z #utc 1970-01-01T00:00:00Z) (- #utc 1970-01-01T00:00:01Z #dur PT2S) (- #dur PT3S #dur PT1S) (- #dur PT1S)
#dur PT1S #utc 1969-12-31T23:59:59Z #dur PT2S #dur -PT1S
(* #dur PT1.5S 2) (* 2 #dur PT1.5S) (* #dur PT1S) (* 2 #dur PT3S 2)
#dur PT3S #dur PT3S #dur PT1S #dur PT12S
(/ #dur PT3S 2) (/ #dur PT0.0000001S -2) (/ #dur -PT0.0000003S 2) (/ #dur PT3S #dur PT2S)
#dur PT1.5S #dur PT0S #dur -PT0.0000001S 1.5
math/abs accepts durations. math/min, math/max, and math/clamp accept
matching UTC or duration kinds and preserve the selected value's kind.
They compare all ticks exactly.
(math/abs #dur -PT1S) (math/abs #dur PT0S) (math/abs #dur PT1.5S)
#dur PT1S #dur PT0S #dur PT1.5S
(math/min #dur PT2S #dur PT1S) (math/min #utc 1970-01-02T00:00Z #utc 1970-01-01T00:00Z) (math/min #dur -PT1S #dur PT0S)
(math/min #dur PT2S #dur PT1S) (math/min #utc 1970-01-02T00:00:00Z #utc 1970-01-01T00:00:00Z) (math/min #dur -PT1S #dur PT0S)
#dur PT1S #utc 1970-01-01T00:00:00Z #dur -PT1S
(math/max #dur PT2S #dur PT1S) (math/max #utc 1970-01-02T00:00Z #utc 1970-01-01T00:00Z) (math/max #dur -PT1S #dur PT0S)
(math/max #dur PT2S #dur PT1S) (math/max #utc 1970-01-02T00:00:00Z #utc 1970-01-01T00:00:00Z) (math/max #dur -PT1S #dur PT0S)
#dur PT2S #utc 1970-01-02T00:00:00Z #dur PT0S
(math/clamp #dur PT3S #dur PT1S #dur PT2S) (math/clamp #utc 1970-01-01T00:00Z #utc 1970-01-02T00:00Z #utc 1970-01-03T00:00Z) (math/clamp #dur PT1.5S #dur PT1S #dur PT2S)
(math/clamp #dur PT3S #dur PT1S #dur PT2S) (math/clamp #utc 1970-01-01T00:00:00Z #utc 1970-01-02T00:00:00Z #utc 1970-01-03T00:00:00Z) (math/clamp #dur PT1.5S #dur PT1S #dur PT2S)
#dur PT2S #utc 1970-01-02T00:00:00Z #dur PT1.5S
12.3.1. Rejected combinations and bounds ↑
UTC plus UTC, duration minus UTC, and numeric additions to durations are type errors. Durations multiply only by integers, and division requires an integer or a duration as the divisor. There is no implicit conversion from seconds or another numeric unit; construct a duration explicitly. Zero divisors are errors.
(+ #utc 1970-01-01T00:00Z #utc 1970-01-01T00:00Z)
(+ #utc 1970-01-01T00:00:00Z #utc 1970-01-01T00:00:00Z)
#err <mismatch type>
(- #dur PT1S #utc 1970-01-01T00:00Z)
(- #dur PT1S #utc 1970-01-01T00:00:00Z)
#err <mismatch type>
(math/min #dur PT1S #utc 1970-01-01T00:00Z)
(math/min #dur PT1S #utc 1970-01-01T00:00:00Z)
#err <mismatch type>
(+ #dur PT922337203685.4775807S #dur PT0.0000001S)
(+ #dur P10675199DT2H48M5.4775807S #dur PT0.0000001S)
#err <integer overflow>
(/ #dur -PT922337203685.4775808S -1)
(/ #dur -P10675199DT2H48M5.4775808S -1)
#err <integer overflow>
(math/abs #dur -PT922337203685.4775808S)
(math/abs #dur -P10675199DT2H48M5.4775808S)
#err <integer overflow>
12.4. Calendar operations ↑
Calendar fields and boundaries use UTC. Week boundaries begin on Monday.
12.4.1. time/parts ↑
Function(time/parts timestamp) → map
Returns integer fields year, month, day, hour, minute, second,
nanosecond, and weekday. Months and days start at one. ISO weekdays run from
one for Monday through seven for Sunday. The nanosecond field is the fractional
part of the second, from zero through 999999900 in multiples of 100.
(time/parts #utc 2024-02-29T12:34:56.1234567Z) (time/parts #utc 1970-01-01T00:00:00Z) (time/parts #utc 1969-12-31T23:59:59.9999999Z)
{day 29 hour 12 minute 34 month 2 nanosecond 123456700 second 56 weekday 4 year 2024} {day 1 hour 0 minute 0 month 1 nanosecond 0 second 0 weekday 4 year 1970} {day 31 hour 23 minute 59 month 12 nanosecond 999999900 second 59 weekday 3 year 1969}
12.4.2. time/start-of ↑
Function(time/start-of timestamp unit) → UTC
Returns the start of the containing UTC calendar unit. The unit is one of
year, month, week, day, hour, minute, or second.
This floors to the earlier boundary, including for times before the Unix epoch.
A week's Monday can fall in the preceding month or year.
(time/start-of #utc 2026-05-06T12:34:56.123Z year) (time/start-of #utc 2026-05-06T12:34:56.123Z month) (time/start-of #utc 2026-01-01T12:34:56Z week) (time/start-of #utc 2026-05-06T12:34:56.123Z day) (time/start-of #utc 2026-05-06T12:34:56.123Z hour) (time/start-of #utc 2026-05-06T12:34:56.123Z minute) (time/start-of #utc 1969-12-31T23:59:59.999Z second)
#utc 2026-01-01T00:00:00Z #utc 2026-05-01T00:00:00Z #utc 2025-12-29T00:00:00Z #utc 2026-05-06T00:00:00Z #utc 2026-05-06T12:00:00Z #utc 2026-05-06T12:34:00Z #utc 1969-12-31T23:59:59Z
(time/start-of #utc 1970-01-01T00:00Z fortnight)
(time/start-of #utc 1970-01-01T00:00:00Z fortnight)
#err <invalid value>
12.4.3. time/shift ↑
Function(time/shift timestamp changes) → UTC
Shifts by a map containing integer years, months, and days offsets.
Omitted offsets are zero; negative offsets move backward. Unknown fields and
non-integer offsets are errors.
The year and month offsets are combined into one month shift. The original day
is then clamped to the last valid day of the resulting month, and the day offset
is applied afterward. Time of day and the 100 ns fraction are retained. For example,
January 31 plus one month becomes February 28 or 29. February 29, 2024 plus one
year becomes February 28, 2025. This uses the earlier valid date when the target
month is shorter; SQLite's default ceiling rule
instead resolves that leap-day example to March 1. Combining years 1 and
months -1 shifts February 29, 2024 to January 29, 2025, with only one clamp.
(time/shift #utc 2024-01-31T12:34:56.123Z {months 1}) (time/shift #utc 2023-01-31T12:00Z {months 1}) (time/shift #utc 2024-01-31T12:00Z {days 1 months 1}) (time/shift #utc 2024-02-29T12:00Z {months -1 years 1}) (time/shift #utc 2024-03-31T12:00Z {months -1}) (time/shift #utc 2024-02-29T12:00Z {years 1})
(time/shift #utc 2024-01-31T12:34:56.123Z {months 1}) (time/shift #utc 2023-01-31T12:00:00Z {months 1}) (time/shift #utc 2024-01-31T12:00:00Z {days 1 months 1}) (time/shift #utc 2024-02-29T12:00:00Z {months -1 years 1}) (time/shift #utc 2024-03-31T12:00:00Z {months -1}) (time/shift #utc 2024-02-29T12:00:00Z {years 1})
#utc 2024-02-29T12:34:56.123Z #utc 2023-02-28T12:00:00Z #utc 2024-03-01T12:00:00Z #utc 2025-01-29T12:00:00Z #utc 2024-02-29T12:00:00Z #utc 2025-02-28T12:00:00Z
(time/shift #utc 1970-01-01T00:00Z {hours 1})
(time/shift #utc 1970-01-01T00:00:00Z {hours 1})
#err <invalid value>
(time/shift #utc 1970-01-01T00:00Z {months 1.5})
(time/shift #utc 1970-01-01T00:00:00Z {months 1.5})
#err <mismatch type>
12.5. Duration quantization ↑
Both arguments must be durations, and the quantum must be strictly positive. A rounded result outside signed 64-bit ticks is an overflow error.
12.5.1. time/truncate ↑
Function(time/truncate duration quantum) → duration
Returns the multiple of quantum reached by truncating toward zero.
For example, 1.9s becomes 1s and -1.9s becomes -1s at a one-second quantum.
(time/truncate #dur PT1.9S #dur PT1S) (time/truncate #dur -PT1.9S #dur PT1S) (time/truncate #dur PT0.5S #dur PT1S)
#dur PT1S #dur -PT1S #dur PT0S
12.5.2. time/round ↑
Function(time/round duration quantum) → duration
Returns the nearest multiple of quantum, with halfway cases rounded away
from zero. At a one-second quantum, 1.5s becomes 2s and -1.5s becomes -2s.
(time/round #dur PT1.5S #dur PT1S) (time/round #dur -PT1.5S #dur PT1S) (time/round #dur PT1.4S #dur PT1S)
#dur PT2S #dur -PT2S #dur PT1S
(time/round (time/duration 9223372036854775807) (time/duration 4))
#err <integer overflow>
(time/now) → UTC
Returns the current wall-clock time as a typed UTC value with 100 ns tick storage. Takes no arguments. The wall clock can change independently of evaluation; this function does not provide a monotonic elapsed-time clock.
To humanize a timestamp relative to the clock, pass the returned UTC explicitly:
(strings/humanize #utc 2026-10-29T12:00Z {mode relative now (time/now)})
Use the String library's strings/humanize function for readable
durations or absolute and relative timestamps. Relative formatting requires an
explicit UTC reference time and never reads the clock implicitly.
Appendix A: Function Index ↑
- * — Returns the product of its arguments.
(*) → integer
(* value) → value
(* value other more*) → value - + — Returns the sum of its arguments.
(+) → integer
(+ value) → value
(+ value other more*) → value - - — With one argument, returns its negation.
With additional arguments, subtracts each from the running result in order.
At least one argument is required. Numeric operands follow the arithmetic rules
above; temporal operands use the combinations in the table.
Integer overflow and non-finite floating-point results are errors, including
when negating the minimum integer.
Unary subtraction also negates durations; UTC timestamps cannot be negated.
(- value more*) → value - / — Returns
ndivided bydand each additional divisor inmore, or the reciprocal ofnwhen called with one argument.(/ n) → float
(/ value divisor more*) → value - < — Returns whether the arguments are strictly increasing.
(< first second more*) → boolean - <= — Returns whether the arguments are nondecreasing.
(<= first second more*) → boolean - = — Returns
trueif all supplied values are equal, otherwisefalse.(= x) → boolean
(= x y) → boolean
(= x y more*) → boolean - > — Returns whether the arguments are strictly decreasing.
(> first second more*) → boolean - >= — Returns whether the arguments are nonincreasing.
(>= first second more*) → boolean - and — Evaluates forms once each, from left to right, and returns the first falsey
value without evaluating later forms; an
#errvalue is falsey, soandreturns it. If every form is truthy, returns the last value. With no forms, returnstrue.(and forms*) → value - apply — Calls a function value with the elements of a list as its arguments.
(apply function arguments) → value - boolean — Returns
falseifvalueisnullorfalse, otherwisetrue.(boolean value) → boolean - boolean? — Returns
trueifbis eithertrueorfalse, otherwisefalse.(boolean? b) → boolean - collections/copy — Returns a copy of
valuethat changes to either leave the other unchanged. Copies every list and map reachable fromvalue. A list or map reached more than once is copied once, so collections shared withinvaluestay shared within the copy, and cycles stay cycles. With the modeshallow, copies only the top-level list or map; its members are shared. Other values do not change and are returned as they are.(collections/copy value) → value
(collections/copy value shallow) → value - collections/update — Replaces the value at
pathinrootwith the result of callingfon that value andargs, in place, and returns the same root collection. Stores the result asputdoes, including container creation and list growth. A missing entry suppliesnulltof.(collections/update root path f args*) → collection - compare — Returns
-1,0, or1whenfirstsorts before, ties with, or sorts aftersecond, usingsortordering.(compare first second) → integer - comparison/identical? — Returns
trueif its operands refer to the same value, otherwisefalse. Errors raised at the same place with the same tags and message may be the same value. - comparison/not= — Returns the negation of the corresponding equality call. This means that not all
arguments are equal, rather than requiring every pair to differ. One argument
returns
false; no arguments is an error. Equality retains its matching-kind checks and stops comparing after the first unequal pair.(comparison/not= value more*) → boolean - cond — Chooses among pairs of tests and result expressions.
(cond test result ...) - const? — Returns
trueifvis a constant reference, such as the quoted form`:name, otherwisefalse. A local and a constant with the same name are not equal.(const? v) → boolean - contains? — Returns
trueifpathselects a value inroot, includingnullorfalse, otherwisefalse.(contains? root path) → boolean - count — Returns the number of list elements, pointer segments, map members, or Unicode string code points.
For
null, returns zero.(count collection) → integer - distinct — Returns a fresh list retaining the first occurrence of each distinct typed
value. Collections compare structurally, with map member order ignored.
Kinds remain distinct: integer
1and float1.0are separate values. Functions compare by identity. Positive and negative floating zero compare equal.(distinct list) → list - do — Evaluates the
exprforms in order and returns the last result.(do expr*) → value - empty? — Returns whether
countis zero. Lists, pointers, maps, strings, andnullare accepted.(empty? collection) → boolean - error? — Returns
trueifvalueis an error andfalseotherwise. Errors can be passed as arguments without losing their tags.(error? value) → boolean - export — Names the top-level locals that a library file provides. When a runtime
loads the file under a namespace, as
!load lib/shapes sdoes in the Epsilon REPL, each named local becomes the constant:s/name, with the value the local has when the file has run. The other locals of the file stay private, and the file's functions read them, not the locals of the code that calls them.(export name*) → list - false? — Returns
trueifvalueis the booleanfalse, otherwisefalse.(false? value) → boolean - first — Returns the first element or pointer segment, or
nullwhen empty.(first list) → value - float — Converts an integer to a float, returns a float unchanged, or parses a signed
decimal string with an optional decimal point and exponent. Integer conversion
can lose precision; for example, integer
9007199254740993converts to9007199254740992.0. Whitespace, hexadecimal notation, NaN, and infinity are rejected. Overflow is an error; underflow to zero is permitted. Booleans, null, and nominal types such as links are rejected.(float value) → float - float? — Returns
trueifnis a floating-point number, otherwisefalse.(float? n) → boolean - fn — Compiles a function literal into a closure.
(fn name? doc-or-meta? [params*] body*) → function
(fn name? doc-or-meta? ([params*] body*)+) → function - fn/comp — Returns a function composing its inputs right to left, passing all call arguments to the rightmost function and each result to the next.
With no inputs, returns
fn/identity. With one, returnsf.(fn/comp) → function
(fn/comp f) → function
(fn/comp f g functions*) → function - fn/complement — Returns a function that calls
fwith its arguments and negates the result as a boolean.(fn/complement f) → function - fn/constantly — Returns a function that ignores its arguments and returns
value.(fn/constantly value) → function - fn/fnull — Returns a function that calls
f, replacingnullin its first three argument positions with the supplied defaultsx,y, andz. Preservesfalseand does not supply missing arguments.(fn/fnull f x) → function
(fn/fnull f x y) → function
(fn/fnull f x y z) → function - fn/identity — Returns
valueunchanged.(fn/identity value) → value - fn/juxt — Returns a function that applies
fandfunctionsto the same arguments and returns their results in order as a list.(fn/juxt f functions*) → function - fn/partial — Returns a function that calls
fwithargsfollowed by its call-time arguments. With no fixed arguments, returnsf.(fn/partial f args*) → function - fn? — Returns
trueiffis a function, otherwisefalse.(fn? f) → boolean - for — Evaluates a body for each combination of collection values and returns a list of the results.
(for [name collection ...] body) - format — Returns a string by substituting
argsinto the conversion specifications intemplate.(format template args*) → string - from-pairs — Returns a fresh map from
[key value]pairs inlist. Later duplicate keys win.(from-pairs list) → map - get — Returns the value selected by
pathinroot, ordefaultif an entry is missing. Withoutdefault, a missing entry returnsnull. A selected value ofnullorfalseis returned unchanged, not replaced bydefault.getdoes not modifyrootor copy the selected value.(get root path) → value
(get root path default) → value - hash/digest — Returns a
#hashofvalueunderalgorithm, a name from this table:(hash/digest algorithm value) → hash - if — Selects a branch according to the truthiness of a test.
(if test then)
(if test then else) - int — Returns an integer unchanged, truncates a float toward zero, or parses a signed
decimal string exactly. String input accepts an optional ASCII sign followed
by ASCII decimal digits, including leading zeros. Whitespace, decimal points,
exponents, and hexadecimal notation are rejected. Values outside signed 64-bit
range are errors. Booleans, null, and nominal types such as links are rejected.
(int value) → integer - int? — Returns
trueifnis an integer, otherwisefalse. A link scalar is not an integer.(int? n) → boolean - jsonptr — Converts RFC 6901 text to a pointer. The empty string gives the root pointer;
a nonempty string must start with
/and contain only valid~0and~1escapes. Invalid text gives#err <invalid value>; a non-string gives#err <mismatch type>.(jsonptr text) → pointer - jsonptr? — Returns
truefor a pointer andfalsefor other values. A pointer is not a list, even though the list functions accept it.(jsonptr? value) → boolean - keys — Returns the map's string keys in ascending Unicode order.
(keys map) → list - let — Introduces local bindings and evaluates a body in their scope.
(let [bindings*] exprs*) - list? — Returns
trueifxsis a list, otherwisefalse.(list? xs) → boolean - lists/any? — Returns
trueat the first truthy predicate result without visiting later elements. Returnsfalsefor an empty list.(lists/any? predicate list) → boolean - lists/append — Appends
valuestolistin place as individual elements and returns the same list. Values are stored without copying. With no additional values, leaveslistunchanged.listmust be a list, notnullor a pointer.(lists/append list values*) → list - lists/concat — Returns a fresh list containing the elements of each input list, in order.
With no inputs, returns an empty list.
(lists/concat lists*) → list - lists/drop — Returns a fresh list excluding the first
nelements oflist.(lists/drop n list) → list - lists/drop-while — Returns
listfrom the first element for whichpredicateis falsey, skipping the preceding elements.(lists/drop-while predicate list) → list - lists/every? — Returns
falseat the first falsey predicate result without visiting later elements. Returnstruefor an empty list.(lists/every? predicate list) → boolean - lists/filter — Calls
predicateon each element, from left to right, and returns the elements whose results are truthy. Retains their input order.(lists/filter predicate list) → list - lists/find-first — Returns the first element of
listfor whichpredicateis truthy, ordefaultif none matches. The default isnull.(lists/find-first predicate list) → value
(lists/find-first predicate list default) → value - lists/flatten — Returns a list of the non-list elements in
list, recursively expanding nested lists in order. Empty lists contribute no elements. Non-list elements, including maps, strings, andnull, are kept unchanged.listmust be a list, otherwise produces#err <mismatch type>. Missing or extra arguments produce#err <arity mismatch>.(lists/flatten list) → list - lists/frequencies — Counts each string in the input list and returns a map from strings to integer
counts. Non-string elements are errors; integer
1is never coerced to string"1".(lists/frequencies strings) → map - lists/group-by — Calls
functionon each element, from left to right. Each result must be a string and becomes a map key. Each map value is the list of input elements with that key, preserving their input order. Non-string keys are errors.(lists/group-by function list) → map - lists/interleave — Returns a fresh list taking one element from each input list in turn, stopping at the shortest list.
Returns
[]with no inputs and a shallow copy with one.(lists/interleave) → list
(lists/interleave list) → list
(lists/interleave list1 list2) → list
(lists/interleave list1 list2 lists*) → list - lists/interpose — Returns a fresh list with
separatorbetween adjacent elements oflist.(lists/interpose separator list) → list - lists/keep — Returns the results of applying
fto each element oflist, excludingnullbut preservingfalseand input order.(lists/keep f list) → list - lists/last — Returns the last element, or
nullfor an empty list.(lists/last list) → value - lists/mapcat — Applies
fto corresponding elements oflistand any additionallists, then concatenates the result lists one level deep. Stops at the shortest input list.(lists/mapcat f list lists*) → list - lists/nth — Returns the element at the nonnegative, zero-based integer
index. An out-of-range index returnsdefaultif supplied; otherwise it is an error. A negative index is an error even when a default is supplied.(lists/nth list index default?) → value - lists/partition — Returns groups of up to
nelements fromlist, startingstepelements apart and including short trailing groups.stepdefaults ton.(lists/partition n list) → list
(lists/partition n step list) → list - lists/partition-by — Returns consecutive groups from
list, starting a new group wheneverfreturns a different value.(lists/partition-by f list) → list - lists/range — Returns integers from
startup to, but excluding,end, incrementing bystep. The defaults are zero forstartand one forstep. A negative step produces a descending range. A zero step is an error. All arguments must be integers.(lists/range end) → list
(lists/range start end step?) → list - lists/repeat — Returns a list containing
valuethe specified number of times.countmust be a nonnegative integer. Values are reused rather than deep copied.(lists/repeat count value) → list - lists/reverse — Reverses
listin place and returns the same list, without copying the elements.listmust be a list, notnullor a pointer.(lists/reverse list) → list - lists/second — Returns the second element of
list, ornulliflisthas fewer than two elements.(lists/second list) → value - lists/slice — Returns a fresh list from
startup to, but excluding,end. Indexes are nonnegative, zero-based integers. An omitted end means the list's length. Indexes beyond the length are clamped, and an end before the start produces an empty list.(lists/slice list start end?) → list - lists/some — Returns the first truthy result of applying
fto elements oflist, without visiting later elements, ornullif none matches.(lists/some f list) → value - lists/sort-by — Sorts
listin place, stably, by the keys returned byf, and returns the same list. The list's order changes only after key extraction and all comparisons succeed.listmust be a list, notnullor a pointer. Callsfonce per element, from left to right, before making comparisons. The key function must be an ordinary function and must not change the input list or its elements. Usessortordering, or the suppliedcomparatorapplied to the keys. Equal keys keep their input order. Elements are shared without copying. Key-function errors stop processing and are returned.(lists/sort-by f list) → list
(lists/sort-by f comparator list) → list - lists/take — Returns a fresh list of the first
nelements oflist, or all elements if fewer are available.(lists/take n list) → list - lists/take-while — Returns the leading elements of
listfor whichpredicateis truthy, stopping at the first falsey result.(lists/take-while predicate list) → list - local — Defines a top-level local: a binding that the later top-level forms of the
same file, or of the same REPL session, read as
.name.(local name value?) → local reference - local? — Returns
trueifvis a local reference, such as the quoted form`.name, otherwisefalse.(local? v) → boolean - map — Returns a list of the results of applying
fto corresponding elements ofxsand any additionallists.(map f xs) → list
(map f xs lists*) → list - map? — Returns
trueifmis a map, otherwisefalse.(map? m) → boolean - maps/select-keys — Returns a fresh map containing the literal top-level keys in
keysthat are present inmap.(maps/select-keys map keys) → map - maps/zipmap — Returns a fresh map pairing string
keyswithvalues, stopping at the shorter list. Later duplicate keys win.(maps/zipmap keys values) → map - math/abs — Returns the absolute value, preserving integer or float kind. The absolute
value of the minimum integer overflows and is an error.
Durations are also accepted and return a duration; the minimum tick
duration likewise overflows. UTC timestamps are rejected.
(math/abs value) → value - math/acos — Returns the arc cosine of
nin radians, in the range from zero to pi.(math/acos n) → float - math/acosh — Returns the nonnegative inverse hyperbolic cosine of
n.(math/acosh n) → float - math/asin — Returns the arc sine of
nin radians, in the range from negative pi/2 to pi/2.(math/asin n) → float - math/asinh — Returns the inverse hyperbolic sine of
n.(math/asinh n) → float - math/atan — Returns the arc tangent of
nin radians, in the range from negative pi/2 to pi/2.(math/atan n) → float - math/atan2 — Returns the angle of the point
(x, y)in radians, in the range from negative pi to pi, using both signs to select the quadrant.(math/atan2 y x) → float - math/atanh — Returns the inverse hyperbolic tangent of
n.(math/atanh n) → float - math/cbrt — Returns the real cube root of
n, with the same sign asn.(math/cbrt n) → float - math/ceil — Returns the least integral-valued float greater than or equal to
nafter conversion to a float.(math/ceil n) → float - math/clamp — Returns
valuelimited to the inclusive interval fromlowthroughhigh. All three arguments must have the same numeric or temporal kind, andlowmust not exceedhigh.(math/clamp value low high) → value - math/copy-sign — Returns the absolute value of
magnitudewith the sign ofsign, including the sign of floating-point zero.(math/copy-sign magnitude sign) → float - math/cos — Returns the cosine of
anglein radians.(math/cos angle) → float - math/cosh — Returns the hyperbolic cosine of
n.(math/cosh n) → float - math/degrees — Returns the angle
radiansconverted to degrees.(math/degrees radians) → float - math/even? — Returns
trueif integernis even, otherwisefalse.(math/even? n) → boolean - math/exp — Returns e raised to
n.(math/exp n) → float - math/exp2 — Returns two raised to
n.(math/exp2 n) → float - math/expm1 — Returns e raised to
nminus one, computed without the loss of precision from subtracting one frommath/exp's rounded result near zero.(math/expm1 n) → float - math/floor — Returns the greatest integral-valued float less than or equal to
nafter conversion to a float.(math/floor n) → float - math/floor-div — Returns the integer quotient of
ndivided byd, rounded toward negative infinity.(math/floor-div n d) → integer - math/floor-mod — Returns the integer remainder of
ndivided bydusing a quotient rounded toward negative infinity.(math/floor-mod n d) → integer - math/hypot — Returns the square root of
x*x + y*ywithout intermediate overflow or underflow.(math/hypot x y) → float - math/inverse-lerp — Returns the fraction locating
valuebetweenstartandend, without clamping. Equal endpoints are an error.(math/inverse-lerp start end value) → float - math/lerp — Returns
start + (end - start) * fractionas a float. The fraction is not clamped, so values outside zero through one extrapolate beyond the endpoints.(math/lerp start end fraction) → float - math/log — Returns the natural logarithm of
n, or its logarithm in the suppliedbase. Whenbaseis supplied, both numbers must have the same type. The base must be positive and cannot be one.(math/log n) → float
(math/log n base) → float - math/log10 — Returns the base-ten logarithm of
n.(math/log10 n) → float - math/log1p — Returns the natural logarithm of one plus
n, computed without the loss of precision from rounding1 + nnear zero.(math/log1p n) → float - math/log2 — Returns the base-two logarithm of
n.(math/log2 n) → float - math/max — Returns the greatest argument. Requires at least one argument, and all arguments
must have the same numeric or temporal kind. Integer and temporal comparisons
retain full 64-bit precision.
(math/max value more*) → value - math/min — Returns the least argument. Requires at least one argument, and all arguments
must have the same numeric or temporal kind. Integer and temporal comparisons
retain full 64-bit precision.
(math/min value more*) → value - math/odd? — Returns
trueif integernis odd, otherwisefalse.(math/odd? n) → boolean - math/pow — Returns
baseraised toexponent.(math/pow base exponent) → float - math/radians — Returns the angle
degreesconverted to radians.(math/radians degrees) → float - math/remap — Maps
valuefrom the input interval to the output interval, without clamping. Equal input endpoints are an error. Reversed intervals are accepted.(math/remap input-start input-end output-start output-end value) → float - math/rint — Returns the integral-valued float nearest to
nafter conversion to a float, choosing the even integer in halfway cases.(math/rint n) → float - math/round — Returns the integer nearest to
n, rounding halfway cases away from zero.(math/round n) → integer - math/sign — Returns integer
-1,0, or1according to the argument's sign.(math/sign number) → integer - math/signum — Returns
-1.0for negativen,1.0for positiven, or zero withn's sign for zero.(math/signum n) → float - math/sin — Returns the sine of
anglein radians.(math/sin angle) → float - math/sinh — Returns the hyperbolic sine of
n.(math/sinh n) → float - math/sqrt — Returns the square root of
n.(math/sqrt n) → float - math/tan — Returns the tangent of
anglein radians.(math/tan angle) → float - math/tanh — Returns the hyperbolic tangent of
n.(math/tanh n) → float - math/trunc — Returns
nconverted to a float with its fractional part discarded, rounding toward zero.(math/trunc n) → float - math/zero? — Returns
trueifnis zero, otherwisefalse.(math/zero? n) → boolean - not — Returns
trueifvalueisnullorfalse, otherwisefalse.(not value) → boolean - null? — Returns
trueifxisnull, otherwisefalse.(null? x) → boolean - number? — Returns
trueifnis an integer or a float, otherwisefalse. A link scalar is not a number.(number? n) → boolean - or — Evaluates forms once each, from left to right, and returns the first truthy
value without evaluating later forms. If every form is falsey, returns the
last value. With no forms, returns
null.(or forms*) → value - pairs — Returns
[key value]pairs in the same ascending key order used bykeys.(pairs map) → list - patch — Applies JSON Merge Patch member semantics to
mapin place and returns the same map. All arguments must be maps, checked before any patch is applied. With no changes maps, returnsmapunchanged.(patch map changes*) → map - put — Stores
valueatpathinrootin place and returns the same root collection. Inserts or replaces a map entry, or replaces an existing list element. A list index equal to its length appends one element. Larger indices are errors. Values replace rather than merge, andnullis stored rather than deleted. Creates maps, never lists, for missing ornullintermediate values. Integer path segments require an existing list.(put root path value) → collection - quasiquote — Constructs data while evaluating unquotes at the matching nesting depth.
List-element splices insert the elements of their evaluated list.
See Quotation forms for prefix syntax and encoding.
#quasiquote form - quote — Returns the enclosed form as data without evaluating it.
References, invocations, and collection contents retain their form representation.
See Quotation forms for prefix syntax and encoding.
#quote form - random/choose — Returns one item of
list, each with the same chance. An empty list ornullproduces#err <invalid value>.(random/choose generator list) → value - random/float — Returns a float
xwithlo <x < hi=. Both bounds are floats. The function takes the top 53 bits of the next output asuin [0, 1), and maps it tolo + u * (hi - lo). Whenhi - lois too large for a float, it useslo * (1 - u) + hi * u. A result that rounds up tohiis drawn again.(random/float generator lo hi) → float - random/int — Returns an integer
xwithlo <x < hi=, uniform over the range. The range can span all 64-bit integers. Integers use Lemire's multiply-and-reject method, so no value is more likely than another.(random/int generator lo hi) → integer - random/seeded — Returns a new generator for the integer
seed. Every integer is a seed, negative ones included.(random/seeded seed) → generator - random/shuffle — Returns a new list with the items of
listin a uniform random order, and leaveslistunchanged. The order comes from a Fisher–Yates shuffle that swaps each positioni, from the last down to the second, with a position from0toi.nullshuffles to[].(random/shuffle generator list) → list - reduce — Starts with
initialand callsfunctionwith the accumulator and each list element, in list order. Each call's result becomes the next accumulator. Returns the final accumulator. An empty list ornullreturnsinitialwithout calling the function. The first argument must be an ordinary function value and the third must be a list, pointer, ornull. Errors stop reduction immediately: a call that returns an#errvalue ends the reduction, and that value is the result.(reduce function initial list) → value - regex/find — Returns the next match from
matcher, or the first match ofrein strings. Returnsnullif there is no match.(regex/find matcher [opts]) → string, list, map, or null
(regex/find re s [opts]) → string, list, map, or null - regex/find-all — Returns an eager list of successive, non-overlapping matches of
rein strings. Each item uses the capture format ofregex/groups. Returns an empty list if there are no matches. After an empty match, the search first tries a nonempty match at the same position; if there is none, it advances by one code point.(regex/find-all re s [opts]) → list - regex/groups — Returns the current match from
matcherwithout advancing it. Without options and with no capture groups, returns the matched string. Otherwise, returns a list containing the full match followed by each capture in group-number order. An unmatched group isnull. Without a current match, produces an error.(regex/groups matcher [opts]) → string, list, or map - regex/matcher — Returns a new mutable matcher for
rein strings, for use withregex/findandregex/groups. A matcher is an opaque value owned by its runtime. It can be passed between evaluations in that runtime, but it cannot be serialized or used in another runtime. The current match is cleared when a search reaches the end without a match.(regex/matcher re s) → matcher - regex/matches — Returns the match if
rematches the entire strings, otherwisenull. Results use the capture format ofregex/groups.(regex/matches re s [opts]) → string, list, map, or null - regex/pattern — Returns a compiled pattern for string
s, orsunchanged if it is already a pattern. Invalid patterns produce an error.(regex/pattern s) → pattern - regex/quote-replacement — Returns string
replacementescaped for literal use withregex/replaceorregex/replace-first.(regex/quote-replacement replacement) → string - regex/replace — Returns string
swith all non-overlapping matches ofrereplaced byreplacement.(regex/replace s re replacement [opts]) → string - regex/replace-first — Returns string
swith the first match ofrereplaced byreplacement. Uses the replacement rules ofregex/replace.(regex/replace-first s re replacement [opts]) → string - regex/split — Returns a list of parts of string
ssplit on matches ofre. The integerlimitdefaults to zero. A positive limit permits at most that many parts, with the unsplit suffix in the last part. Zero omits trailing empty strings. A negative limit preserves them. A zero-width match at the start ofsdoes not create a leading empty part. If no split occurs, the result containssas its only part.(regex/split s re) → list
(regex/split s re limit) → list - rem — Returns the remainder with the sign of the dividend. Two integers produce an
exact integer remainder; otherwise the result is a float. A zero divisor is an
error. The minimum integer divided by
-1has remainder zero.(rem dividend divisor) → number - remove — Removes the entry selected by
pathfromrootin place and returns the same root collection. Removes a map member or a list element, shifting later list elements left. Empty parent collections are not removed. A missing key, out-of-range nonnegative index, or missing ornullintermediate value leavesrootunchanged. No containers are created.(remove root path) → collection - rest — Returns a fresh sequence without the first element. A pointer produces a
pointer; an empty list produces
[]and an empty pointer produces the root pointer.(rest list) → list - sort — Sorts
listin place, stably, and returns the same list, without copying its elements. The list's order changes only after all comparisons succeed.listmust be a list, notnullor a pointer. By default, sorts in ascending order and requires all elements to have the same kind. Orderable kinds are null, boolean, integer, float, string, link, UTC, duration, SHA-256, and pointer. Pointer segments compare lexicographically, with an index before a key; a shorter prefix sorts before its extension. Integers and nominal integer payloads compare exactly. Strings compare by Unicode code point order, booleans putfalsebeforetrue, and SHA-256 values compare their bytes. Null values tie.(sort list) → list
(sort comparator list) → list - str — Converts zero or more
valuesto strings and concatenates them in argument order. It inserts no spaces or other separators. With no arguments, returns the empty string.(str values*) → string - string? — Returns
trueifsis a string, otherwisefalse.(string? s) → boolean - strings/blank? — Returns
trueifsisnull, empty, or contains only whitespace, otherwisefalse.(strings/blank? s) → boolean - strings/case — Converts
valuewith one case selector or a list of selectors. A list applies the conversions from left to right. An empty list returns the input unchanged. The selector and input must be strings. Unknown selectors produce an error.(strings/case case value) → string
(strings/case cases value) → string - strings/ends-with? — Returns whether the string ends with the literal
suffix. Every string ends with the empty string.(strings/ends-with? string suffix) → boolean - strings/escape — Escapes text for the specified mode. With only a mode, returns a unary function
that escapes its argument, for use as a template's
escapeoption. The function prints and hashes as its call, such as(strings/escape html-text), and renders a template argument before escaping it.(strings/escape mode text) → string
(strings/escape mode) → function - strings/from-code-points — Returns the string formed by the Unicode scalar values in the list
points, in order.(strings/from-code-points points) → string - strings/humanize — Formats an integer, float, duration, or UTC value as readable text. Options are
a map with string keys. Unknown options and options irrelevant to the value's
kind are errors.
(strings/humanize value options?) → string - strings/includes? — Returns whether the string contains the literal
search. Every string includes the empty string.(strings/includes? string search) → boolean - strings/index-of — Returns the first index of the string
valueinsat or afterstart, ornullif not found.(strings/index-of s value) → integer or null
(strings/index-of s value start) → integer or null - strings/join — Joins a list of strings with
separatorbetween consecutive elements. An empty list produces the empty string. Elements and the separator must be strings.(strings/join strings separator) → string - strings/last-index-of — Returns the last index of the string
valueinsat or beforestart, ornullif not found.(strings/last-index-of s value) → integer or null
(strings/last-index-of s value start) → integer or null - strings/normalize — Returns
snormalized to the Unicode normalizationform, defaulting toNFC.(strings/normalize s) → string
(strings/normalize s form) → string - strings/replace — Replaces non-overlapping literal occurrences of
search, from left to right. An empty search insertsreplacementbefore, between, and after the input's Unicode code points:(strings/replace ab "" x)returnsxaxbx.(strings/replace string search replacement) → string - strings/replace-first — Returns
swith the first occurrence ofmatchreplaced byreplacement.(strings/replace-first s match replacement) → string - strings/split — Splits at each literal occurrence of
separator, preserving empty fields. An empty separator splits into Unicode code points; splitting an empty string with an empty separator returns a list containing the empty string.(strings/split string separator) → list
(strings/split string separator limit) → list - strings/split-lines — Returns the lines of
sseparated by LF or CRLF, without their line endings.(strings/split-lines s) → list - strings/starts-with? — Returns whether the string begins with the literal
prefix. Every string starts with the empty string.(strings/starts-with? string prefix) → boolean - strings/substring — Returns the code points from
startup to, but excluding,end. Withoutend, returns the rest of the string. Both indices must be integers between zero and the string length, andstartmust not exceedend. Other indices produce#err <bounds index>.(strings/substring string start) → string
(strings/substring string start end) → string - strings/trim — Returns
swith whitespace ortextremoved from the ends selected byopts.(strings/trim opts s) → string
(strings/trim opts text s) → string - strings/unescape — Decodes text for the same modes as
strings/escape. With only a mode, returns a unary decoding function that prints and hashes as its call, such as(strings/unescape url-query-value). Invalid JSON Pointer, URL percent, CSV, and quoted string escapes produce#err <invalid value>, as do URL percent escapes whose bytes are not UTF-8, since strings are always Unicode text. HTML decoding recognizes semicolon-terminated named and numeric character references.(strings/unescape mode text) → string
(strings/unescape mode) → function - template? — Returns
trueifvalueis a string template, otherwisefalse.(template? value) → boolean - time/duration — With one argument, a number supplies a raw 100 ns tick count and a string
supplies an ISO duration. A float is rounded to the nearest tick, with halfway
cases rounded away from zero and a minimum of one tick in magnitude for
nonzero values:
(time/duration 1.5)is two ticks.(time/duration value) → duration
(time/duration amount unit) → duration - time/duration? — Returns whether the value has the duration kind. UTC timestamps and integers
return
false.(time/duration? value) → boolean - time/format — Formats a UTC timestamp or duration as its canonical ISO string, without a
DUST tag prefix. The full 100 ns tick precision is retained. For readable display text,
use the String library's
strings/humanizefunction.(time/format value) → string - time/now — Returns the current wall-clock time as a typed UTC value with 100 ns tick
storage. Takes no arguments. The wall clock can change independently of
evaluation; this function does not provide a monotonic elapsed-time clock.
(time/now) → UTC - time/parts — Returns integer fields
year,month,day,hour,minute,second,nanosecond, andweekday. Months and days start at one. ISO weekdays run from one for Monday through seven for Sunday. The nanosecond field is the fractional part of the second, from zero through999999900in multiples of 100.(time/parts timestamp) → map - time/round — Returns the nearest multiple of
quantum, with halfway cases rounded away from zero. At a one-second quantum,1.5sbecomes2sand-1.5sbecomes-2s.(time/round duration quantum) → duration - time/shift — Shifts by a map containing integer
years,months, anddaysoffsets. Omitted offsets are zero; negative offsets move backward. Unknown fields and non-integer offsets are errors.(time/shift timestamp changes) → UTC - time/start-of — Returns the start of the containing UTC calendar unit. The unit is one of
year,month,week,day,hour,minute, orsecond. This floors to the earlier boundary, including for times before the Unix epoch. A week's Monday can fall in the preceding month or year.(time/start-of timestamp unit) → UTC - time/truncate — Returns the multiple of
quantumreached by truncating toward zero. For example,1.9sbecomes1sand-1.9sbecomes-1sat a one-second quantum.(time/truncate duration quantum) → duration - time/units — Expresses a UTC timestamp or duration in the requested unit. The unit argument
comes first. For UTC, the result measures time since the Unix epoch; for a
duration, it measures the elapsed interval. The underlying value remains a
signed count of 100 ns ticks.
(time/units unit value) → number - time/utc — An integer supplies a raw count of 100 ns ticks since the Unix epoch. A string
supplies a UTC timestamp ending in
Z. A float supplies a Julian day, following SQLite's Julian-day convention: day0.0is-4713-11-24T12:00:00Z,2440587.5is the Unix epoch, and2451545.0is2000-01-01T12:00:00Z.(time/utc value) → UTC - time/utc? — Returns whether the value has the UTC kind. Durations and integers return
false.(time/utc? value) → boolean - true? — Returns
trueifvalueis the booleantrue, otherwisefalse.(true? value) → boolean - unquote — Evaluates the enclosed form within its matching quasiquote and substitutes
the resulting value. Outside quasiquote, this is an error.
See Quotation forms for prefix syntax and encoding.
#unquote form - unquote-splicing — Evaluates the enclosed list within its matching quasiquote and inserts its
elements into the surrounding list. A non-list result or a splice outside a
list-element position is an error.
See Quotation forms for prefix syntax and encoding.
#unquote-splicing form - values — Returns map values in the same ascending key order used by
keys.(values map) → list - when — Runs a body when its test is truthy.
(when test body...) - when-not — Runs a body when its test is falsey.
(when-not test body...)