DUST

Table of Contents

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.

DUST
(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)])
→
Canonical DUST
(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)])
=
DUST Result
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:

DUST
[hello ".-#1" don't true false null
 42 -1.5 "true" "false" "null" "42"
 "-1.5" nil "#name" "^name" .name
 🙂 "" {true false}]
→
Canonical DUST
hello
".-#1"
don't
true
false
null
42
-1.5
"true"
"false"
"null"
"42"
"-1.5"
nil
"#name"
^name
.name
🙂
""
<!true>
→
RON
[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:

DUST
[
  Ada Lovelace
  Grace Hopper
]
→
Canonical DUST
"Ada Lovelace"
"Grace Hopper"
→
RON
[
  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:

DUST
["say \"hello\"" ""C:\\tmp\\notes.txt"" "\u03b5" "\u0000"]
→
Canonical DUST
""say "hello"""
C:\tmp\notes.txt
ε
"\u0000"
→
RON
[
  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:

DUST
[
  ""hello world""
  """
    hello world
  """
  """
    first line
    second line
  """
]
→
Canonical DUST
"hello world"
"hello world"
"""
  first line
  second line
"""
→
RON
[
  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:

DUST
["^name" "^x" .name :name "^" .name]
→
Canonical DUST
^name
^x
.name
:name
^
.name
→
RON
[^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.

DUST
(+ 1 2)
(* 3 4)
→
RON
[#invoke [+ 1 2] #invoke [* 3 4]]
=

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.

DUST
[[1 2]]
→
RON
[[1 2]]
=
DUST Result
[[1 2]]
DUST
1
[2 3]
→
RON
[1 [2 3]]
=
DUST Result
1
[2 3]

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.

DUST
[[foo(bar)]
 (foo(bar))
 [foo[bar]{}(bar)baz]
 [[.foo(bar)][(bar):foo]]]
→
Canonical DUST
[[foo (bar)] (foo (bar)) [foo [bar] {} (bar) baz] [[.foo (bar)] [(bar) :foo]]]
→
RON
[[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.

DUST
[{items[0] 1 foo(bar) 2 .name 3}]
→
Canonical DUST
[{.name 3 foo(bar) 2 items[0] 1}]
→
RON
[{.name 3
  foo(bar) 2
  items[0] 1}]

A key that begins with ) is quoted, since a line that begins with ) closes a form:

DUST
{"(a" b ")" 1 ")x" 2 "x)" 3 "(" 4 k ["a <b" "c" "a<b"]}
→
Canonical DUST
{( 4
 (a b
 ")" 1
 ")x" 2
 k [
   "a <b"
   c
   a<b]
 x) 3}
→
RON
( 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.

DUST
[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
→
Canonical DUST
[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
                                                                                                                                                                                                                                                                 1]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
=
DUST Result
#err {<internal limit nesting>
      message "program exceeds an internal limit: expressions nest too deeply"}
DUST
(let [count-up (fn count-up [n] (+ 1 (count-up (+ .n 1))))] (count-up 0))
=
DUST Result
#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.

DUST
{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}]}
→
Canonical DUST
{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}
→
RON
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
=
DUST Result
#err <name unbound>
DUST
(let [name Ada] .name)
→
RON
#invoke [let [name Ada] #local name]
=
DUST
:name
→
RON
#const name
→
Evaluation cases
#cases [{bindings {name Ada} output Ada}]
DUST
(let [add :+]
  [(add 0 0)
   (add 1 2)
   (add -4 10)])
→
Canonical DUST
(let [add :+] [(add 0 0) (add 1 2) (add -4 10)])
→
RON
#invoke [let [add #const +] [#invoke [add 0 0] #invoke [add 1 2] #invoke [add -4 10]]]
=
DUST
:42
→
RON
#const "42"
→
Evaluation cases
#cases [{bindings {42 42} output 42}]
DUST
:true
→
RON
#const "true"
→
Evaluation cases
#cases [{bindings {true enabled} output enabled}]
DUST
:null
→
RON
#const "null"
→
Evaluation cases
#cases [{bindings {null missing} output missing}]

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.

DUST
(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))
→
Canonical DUST
(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))
=
DUST Result
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
DUST
(let [p /3/,"3"]
 [.p (get [null null null {3 yes}] .p) (str .p)])
(let [name Ada] (str /,~"hello-,.name"))
(str /,~"inner ,(str "x")")
→
Canonical DUST
(let [p /3/3] [.p (get [null null null {3 yes}] .p) (str .p)])
(let [name Ada] (str /,~"hello-,.name"))
(str /,~"inner ,(str x)")
=
DUST Result
[
  [/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.

DUST
(upper hello)
→
RON
#invoke [upper hello]
→
Evaluation cases
#cases [{bindings {upper (fn [x] .x)} output hello}]
DUST
("upper" hello)
→
RON
#invoke ["""
           "upper"
         """
         hello]
→
Evaluation cases
#cases [{bindings {""\"upper""" (fn [x] .x)} output hello}]
DUST
(+ 1 2)
→
RON
#invoke [+ 1 2]
=
DUST
("+" 1 2)
→
RON
#invoke ["""
           "+"
         """
         1
         2]
→
Evaluation cases
#cases [{bindings {""\"+""" (fn [a b] (+ .a .b))} output 3}]
DUST
(let [twice (fn [x] (* 2 .x))]
  (.twice 21))
→
Canonical DUST
(let [twice (fn [x] (* 2 .x))] (.twice 21))
→
RON
#invoke [let [twice #invoke [fn [x] #invoke [* 2 #local x]]] #invoke [#local twice 21]]
=
DUST
(:upper hello)
→
RON
#invoke [#const upper hello]
→
Evaluation cases
#cases [{bindings {upper (fn [x] .x)} output hello}]
DUST
(:+ 1 2)
→
RON
#invoke [#const + 1 2]
=
DUST
(f :x)
→
RON
#invoke [f #const x]
→
Evaluation cases
#cases [{bindings {f (fn [value] .value) x Ada} output Ada}]
DUST
[upper hello]
→
Canonical DUST
upper
hello
→
RON
[upper hello]
=
DUST Result
upper
hello

A computed callee is encoded as the first form in #invoke. For example:

DUST
((if true :+ :*) 2 3)
→
RON
#invoke [#invoke [if true #const + #const *] 2 3]
=

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.

DUST
()
→
RON
#invoke []
DUST
(
  .f
  hello world
)
→
Canonical DUST
(.f hello world)
→
RON
#invoke [#local f hello world]
DUST
(let [
  first name
  second name
] [first name])
→
Canonical DUST
(let
  [
    first name
    second name]
  [first name])
→
RON
#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
DUST
`.name
→
RON
#quote #local name
=
DUST
~[a .b]
→
RON
#quasiquote [a #local b]
=
DUST
,.name
→
RON
#unquote #local name
=
DUST Result
#err <outside quasiquote unquote>
DUST
,@.items
→
RON
#unquote-splicing #local items
=
DUST Result
#err <outside quasiquote splicing unquote>
DUST
`~(f ,.x ,@.xs)
→
RON
#quote #quasiquote #invoke [f #unquote #local x #unquote-splicing #local xs]
=
DUST Result
~(f ,.x ,@.xs)

Quoting a string does not turn it into a DUST quotation form.

DUST
#quote .name
→→
RON
#quote #local name
=
DUST
#quasiquote [a b]
→→
RON
#quasiquote [a b]
=
DUST
#unquote .name
→→
RON
#unquote #local name
=
DUST Result
#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.

DUST
#link .missing
#link (/ 1 0)
(error? #link .missing)
=
DUST Result
#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.

DUST
(do [#link 0 #link -1 #link -9223372036854775808 #link 9223372036854775807])
=
DUST Result
#link 0
#link -1
#link -9223372036854775808
#link 9223372036854775807
DUST
#link "42"
=
DUST Result
#link "42"
DUST
(int? #link 42)
=
DUST
(number? #link 42)
=
DUST
(+ 1 #link 42)
=
DUST Result
#err <mismatch type>
DUST
(do [(= #link 42 #link 42) (= #link 42 #link 43) (comparison/identical? #link 42 #link 42)])
=
DUST Result
true
false
true
DUST
(= #link 42 42)
=
DUST Result
#err <mismatch type>
DUST
{#utc 2026-10-24T12:34:56Z}
→
Canonical DUST
#utc 2026-10-24T12:34:56Z
=
DUST Result
#utc 2026-10-24T12:34:56Z
DUST
{#utc 2026-10-24T12:34:56Z}
→
Canonical DUST
#utc 2026-10-24T12:34:56Z
→
RON
#utc 2026-10-24T12:34:56Z
=
DUST Result
#utc 2026-10-24T12:34:56Z
DUST
{
  dependsOn {#link 42}
  when {#utc 2026-09-09T00:00:00Z}
}
→
Canonical DUST
{dependsOn #link 42
 when #utc 2026-09-09T00:00:00Z}
→
RON
dependsOn #link 42
when #utc 2026-09-09T00:00:00Z
=
DUST Result
{dependsOn #link 42
 when #utc 2026-09-09T00:00:00Z}
DUST
#dur PT30S
→
RON
#dur PT30S
DUST
#utc 2026-10-24T12:34:56Z
→
RON
#utc 2026-10-24T12:34:56Z

#hash holds a digest tagged with its algorithm; the hash library defines its payload and equality.

DUST
#hash #sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
→
RON
#hash #sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
=
DUST Result
#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.

DUST
(+ 1 #mute (* 20 30) 2)
→
Canonical DUST
(+ 1 \(* 20 30) 2)
→
RON
#invoke [+ 1 #mute #invoke [* 20 30] 2]
DUST
#mute #invoke [+ 1 2 3]
→
Canonical DUST
\(+ 1 2 3)
→
RON
#mute #invoke [+ 1 2 3]
=

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
DUST
(+ 1 #mute (/ 1 0) 2)
→
Canonical DUST
(+ 1 \(/ 1 0) 2)
=
DUST
[1 #mute .missing 2]
→
Canonical DUST
1
\.missing
2
=
DUST
[\.missing \[1 2] \{a 1}]
→
Canonical DUST
\.missing
\[1 2]
\{a 1}
→
RON
[#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.

DUST
#invoke [#mute + 1 2]
→=
DUST Result
#err <callee mute>

To begin a literal string with a backslash, quote the string.

DUST
["\\path" "\\(+ 1 2)"]
→
Canonical DUST
"\\path"
"\\(+ 1 2)"
→
RON
[
  \path
  \(+ 1 2)]
=
DUST Result
"\\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.

DUST
[ (quote 1) (quasiquote [a b]) (unquote .x) (unquote-splicing .xs) ]
→
Canonical DUST
(quote 1)
(quasiquote [a b])
(unquote .x)
(unquote-splicing .xs)
→
RON
[#invoke [quote 1] #invoke [quasiquote [a b]]
 #invoke [unquote #local x] #invoke [unquote-splicing #local xs]]
DUST
{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 []}
→
Canonical DUST
{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)}
→
RON
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]
DUST
(f
  ; before the first argument
  1 ; first argument
  2)
→
RON
"#invoke" [f
           ; before the first argument
           1 ; first argument
           2]

DUST
(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" )
→
Canonical DUST
(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")
→
RON
#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]
Lists use square brackets. Strings use quotes or fences when a bare spelling would change the parsed form, including strings that begin with . or :.

DUST
["foo(bar)" "(bar)" "abc)" ".name" ":name"]
→
Canonical DUST
"foo(bar)"
"(bar)"
"abc)"
".name"
":name"
→
RON
[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.

DUST
(let [name Ada] .name)
=
DUST
.math/pi
=
DUST Result
#err <name unbound>
DUST
(if true 1 .missing)
=
DUST
:name
→
Evaluation cases
#cases [{bindings {name Ada} output Ada}]
DUST
(let [x 1] :x)
=
DUST Result
#err <name unbound>
DUST
(if false :missing 1)
=
DUST Result
#err <name unbound>
DUST
`[:missing .missing]
=
DUST Result
:missing
.missing
DUST
(+ 1 \:missing 2)
=
DUST
:math/pi
→
Evaluation cases
#cases [{bindings {math/pi 3} output #err <conflict name>}]
DUST
(+ 1 2)
→
Evaluation cases
#cases [{bindings {+ 0} output #err <conflict name>}]

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.

DUST
{#tag value}
→
Canonical DUST
#tag value
=
DUST Result
#tag value
DUST
#application {
  symbol .missing
  invocation (unknown-function .missing)
  unquote ,.missing
  splice ,@.missing
  nested #another (unknown-function (/ 1 0))
}
→
Canonical DUST
#application {invocation (unknown-function .missing)
              nested #another (unknown-function (/ 1 0))
              splice ,@.missing
              symbol .missing
              unquote ,.missing}
=
DUST Result
#application {invocation (unknown-function .missing)
              nested #another (unknown-function (/ 1 0))
              splice ,@.missing
              symbol .missing
              unquote ,.missing}
DUST
#application {known #local 42}
=
DUST Result
#err <invalid tag>

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.

DUST
(+ 1 2)
=
DUST
(:+ 1 2)
=
DUST
("+" 1 2)
→
Evaluation cases
#cases [{bindings {""\"+""" (fn [a b] (+ .a .b))} output 3}]
DUST
((if true :+ :*) 2 3)
=
DUST
((if true "+" "*") 2 3)
→
Canonical DUST
((if true + *) 2 3)
=
DUST Result
#err <callee string>
DUST
(let [op (if true :+ :*)]
  (op 2 3))
→
Canonical DUST
(let [op (if true :+ :*)] (op 2 3))
=
DUST
(let [a-fn :+]
  (a-fn 1 2))
→
Canonical DUST
(let [a-fn :+] (a-fn 1 2))
=

(+ 1 (* 2 2)) can be thought of as being evaluated in two steps:

  1. Evaluate (* 2 2) to obtain 4.
  2. Substitute 4 for the multiplication, giving (+ 1 4), which evaluates to 5.

Hence:

DUST
(+ 1 (* 2 2))
=

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.

DUST
[+ 1 2]
→=
DUST
[(+ 1 2) 4]
→
Canonical DUST
(+ 1 2)
4
=
DUST
{total (+ 1 2)}
=
DUST Result
{total 3}

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 #err values 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.6.1. Branching ↑

DUST provides one primitive special form for branching, if. The macros when, when-not, and cond build on it to express common branching patterns.

1.6.2. Looping ↑

Use for to iterate over collections.

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 ↑

1.7.1.1. fn ↑
Special Form

(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 ↑
DUST
(let [increment (fn increment [x]
                   (+ 1 .x))]
  [(increment 0)
   (increment 1)
   (increment 41)])
→
Canonical DUST
(let [increment (fn increment [x] (+ 1 .x))] [(increment 0) (increment 1) (increment 41)])
=
DUST
(let [increment (fn increment "Return x plus one." [x]
                   (+ 1 .x))]
  [(increment 0)
   (increment 1)
   (increment 41)])
→
Canonical DUST
(let
  [increment (fn increment "Return x plus one." [x] (+ 1 .x))]
  [(increment 0) (increment 1) (increment 41)])
=
DUST
(let [increment (fn increment {dust/doc "Return x plus one."} [x]
                   (+ 1 .x))]
  [(increment 0)
   (increment 1)
   (increment 41)])
→
Canonical DUST
(let
  [increment (fn increment {dust/doc "Return x plus one."} [x] (+ 1 .x))]
  [(increment 0) (increment 1) (increment 41)])
=
DUST
(let [increment (fn {dust/doc "Return x plus one."} [x]
                   (+ 1 .x))]
  [(increment 0)
   (increment 1)
   (increment 41)])
→
Canonical DUST
(let
  [increment (fn {dust/doc "Return x plus one."} [x] (+ 1 .x))]
  [(increment 0) (increment 1) (increment 41)])
=
DUST
(let [increment (fn [x] (+ 1 .x))]
  (increment 4))
→
Canonical DUST
(let [increment (fn [x] (+ 1 .x))] (increment 4))
=
DUST
(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 "!")])
→
Canonical DUST
(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 !)])
=
DUST Result
"Hello, world!"
"Hello, Ada!"
"Hi, Ada!"
"Hi, Ada and Bob!"
1.7.1.2. do ↑
Special Form

(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.

DUST
[(do)
 (do 7)
 (do 1 2 3)
 (if true (do ignored 42) (/ 1 0))]
→
Canonical DUST
(do)
(do 7)
(do 1 2 3)
(if true (do ignored 42) (/ 1 0))
=
DUST Result
null
7
3
42

1.7.2. let ↑

Special Form

(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.

DUST
(let [price (* 5 2)
      quantity 3]
  (* .price .quantity))
→
Canonical DUST
(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.

DUST
(let [price 10
      quantity 3
      total (* .price .quantity)]
  .total)
→
Canonical DUST
(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.

DUST
(let [price 10]
  [(let [price (+ .price 2)]
     .price)
   .price])
→
Canonical DUST
(let [price 10] [(let [price (+ .price 2)] .price) .price])
=

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).

DUST
(let [name outer
      read-name (fn [] .name)]
  (let [name inner]
    (read-name)))
→
Canonical DUST
(let [name outer read-name (fn [] .name)] (let [name inner] (read-name)))
=
DUST
(let [read-name (let [name outer]
                  (fn [] .name))]
  (read-name))
→
Canonical DUST
(let [read-name (let [name outer] (fn [] .name))] (read-name))
=

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.

DUST
(let [] 1 2)
=
DUST
(let [x 1])
=

Additional forms that introduce bindings are: fn, for, and local.

1.7.3. local ↑

Special Form

(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.

DUST
(local a 10)
(+ .a 1)
=
DUST
(do (local a 1))
=
DUST Result
#err <invalid program>
DUST
(local a 1 2)
=
DUST Result
#err <arity mismatch>

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.

DUST
(local a 10)
(local f (fn [] .a))
(local a 11)
(.f)
=
DUST Result
.a
.f
.a
11
DUST
(local h (fn [] (k)))
(local k (fn [] 42))
(h)
=
DUST Result
.h
.k
42

(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.

DUST
(local g)
.g
=
DUST Result
.g
#err <name unbound>
DUST
(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)
=
DUST Result
.odd?
.even?
.odd?
true

When the value is an #err value, local returns that value and the local is declared without a value.

DUST
(local bad (/ 1 0))
.bad
=
DUST Result
#err <division integer zero>
#err <name unbound>

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.

DUST
(local a 10)
(let [a 1] [.a (+ .a 1)])
=
DUST Result
.a
[1 2]
DUST
(local a 10)
:a
=
DUST Result
.a
#err <name unbound>
DUST
(local map 1)
=
DUST Result
#err <conflict name>

1.7.4. export ↑

Special Form

(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.

DUST
(local a 1)
(export a)
=
DUST Result
.a
[.a]

1.7.5. Control Flow ↑

1.7.5.1. and ↑
Special Form

(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.

DUST
(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)])
=
DUST Result
true
x
false
null
[y 1]
1.7.5.2. or ↑
Special Form

(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.

DUST
(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)])
=
DUST Result
null
x
0
""
[y 1]
1.7.5.3. if ↑
Special Form

(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.

DUST
(if true accepted rejected)
=
DUST Result
accepted
DUST
(if false accepted rejected)
=
DUST Result
rejected
DUST
(if false accepted)
=
DUST
(if true 42 (/ 1 0))
=
1.7.5.4. when ↑
Macro

(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.

DUST
(when true ready)
=
DUST
(when false ready)
=
DUST
(when true 1 2)
=
DUST
(when false (/ 1 0))
=
DUST
(when true)
=
1.7.5.5. when-not ↑
Macro

(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.

DUST
(when-not false ready)
=
DUST
(when-not null 1 2)
=
DUST
(when-not true ready)
=
1.7.5.6. cond ↑
Macro

(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.

DUST
(cond false first true second)
=
DUST
(cond true false true fallback)
=
DUST
(cond false first)
=
DUST
(cond)
=
1.7.5.7. for ↑
Macro

(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.

DUST
(for [x [1 2 3]]
  (* .x .x))
→
Canonical DUST
(for [x [1 2 3]] (* .x .x))
=

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.

DUST
(for [x [1 2]
      y [10 20]]
  (+ .x .y))
→
Canonical DUST
(for [x [1 2] y [10 20]] (+ .x .y))
=
DUST Result
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.

DUST
(for [xs [[1 2] [3]]
      x .xs]
  .x)
→
Canonical DUST
(for [xs [[1 2] [3]] x .xs] .x)
=
DUST
(for [x []]
  .x)
→
Canonical DUST
(for [x []] .x)
=

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.

DUST
(for [x [1 2]]
  [.x])
→
Canonical DUST
(for [x [1 2]] [.x])
=
DUST Result
[[1] [2]]

1.7.6. Quotation ↑

1.7.6.1. quote ↑
Special Form

#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.

1.7.6.3. unquote ↑
Macro

#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:

2.1. apply ↑

Function

(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.

DUST
(apply :+ [1 2])
=
DUST
(apply (if true :+ :*) [2 3])
=
DUST
(apply "+" [1 2])
→
Canonical DUST
(apply + [1 2])
=
DUST Result
#err <expected function>
DUST
(let [increment (fn [x] (+ 1 .x))]
  (apply .increment [4]))
→
Canonical DUST
(let [increment (fn [x] (+ 1 .x))] (apply .increment [4]))
=

2.2. = ↑

Function

(= 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.
DUST
[(= 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])]
→
Canonical DUST
(= 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])
=
DUST Result
true
true
true
true
true
false
true
false
false
false
false
true
true
DUST
(=)
=
DUST Result
#err <argument missing>
DUST
(= 1 1 "1")
=
DUST Result
#err <mismatch type>
DUST
(= 1 2 (/ 1 0))
=
DUST
(= (/ 1 0))
=
DUST
(= 1 1.0)
=
DUST Result
#err <mismatch type>
DUST
(= hello "hello")
→
Canonical DUST
(= hello hello)
=
DUST
(= [1 2] [1 2])
=
DUST
(= [1 2] [2 1])
=
DUST
(= {x 1} {x 1})
=
DUST
(= {a 1 b 2} {b 2 a 1})
→
Canonical DUST
(= {a 1 b 2} {a 1 b 2})
=
DUST
(= {a b} {a b c null})
=
DUST
(= {<active>} {active true})
→
Canonical DUST
(= <active> <active>)
=
DUST
(= {<!active>} {active false})
→
Canonical DUST
(= <!active> <!active>)
=
DUST
(= {<!active>} {})
→
Canonical DUST
(= <!active> {})
=
DUST
(= `[] [])
=
DUST
(= `[1 2] [1 2])
=
DUST
(let [a {x 1}
      b {x 1}
      c .a]
  (= .a .b))
→
Canonical DUST
(let [a {x 1} b {x 1} c .a] (= .a .b))
=
DUST
(let [a {x 1}
      b {x 1}
      c .a]
  (= .a .c))
→
Canonical DUST
(let [a {x 1} b {x 1} c .a] (= .a .c))
=
DUST
(let [a {x 1} b {x 1} c .a] (patch .a {x 2}))
=
DUST
(let [a {x 1} b {x 1} c .a] (patch .a {x 2}) (= .a .b))
=
DUST
(let [a {x 1} b {x 1} c .a] (patch .a {x 2}) (= .a .c))
=
DUST
(let [f (fn [x] .x)
      g .f
      h (fn [x] .x)]
  (= .f .g))
→
Canonical DUST
(let [f (fn [x] .x) g .f h (fn [x] .x)] (= .f .g))
=
DUST
(let [f (fn [x] .x)
      g .f
      h (fn [x] .x)]
  (= .f .h))
→
Canonical DUST
(let [f (fn [x] .x) g .f h (fn [x] .x)] (= .f .h))
=
DUST
(let [f (fn [x] .x)
      g .f
      h (fn [x] .x)]
  (= .f a))
→
Canonical DUST
(let [f (fn [x] .x) g .f h (fn [x] .x)] (= .f a))
=
DUST Result
#err <mismatch type>
DUST
(let [f (fn [x] .x)
      g .f
      h (fn [x] .x)]
  (= [.f] [.g]))
→
Canonical DUST
(let [f (fn [x] .x) g .f h (fn [x] .x)] (= [.f] [.g]))
=
DUST
(let [f (fn [x] .x)
      g .f
      h (fn [x] .x)]
  (= [.f] [.h]))
→
Canonical DUST
(let [f (fn [x] .x) g .f h (fn [x] .x)] (= [.f] [.h]))
=
DUST
(let [f (fn [x] .x)
      g .f
      h (fn [x] .x)]
  (= {callback .f} {callback .g}))
→
Canonical DUST
(let [f (fn [x] .x) g .f h (fn [x] .x)] (= {callback .f} {callback .g}))
=

2.3. not ↑

Function

(not value) → boolean

Returns true if value is null or false, otherwise false.

DUST
(not true)
=
DUST
(not false)
=
DUST
(not null)
=
DUST
(not 0)
=
DUST
(not [])
=

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.

2.4.1.1. map ↑
Function

(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.

DUST
[(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])]
→
Canonical DUST
(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])
=
DUST Result
[[ADA STRASSE ""] [11 22] [111] [1111] [] [] [] [[1] [2]]]
DUST
(map (fn [x] (strings/case upper .x)))
=
DUST Result
#err <list missing>
DUST
(map upper [ada])
=
DUST Result
#err <expected function>
DUST
(map (fn [x] (strings/case upper .x)) ada)
=
DUST Result
#err <mismatch type>
DUST
(map (fn [x] (/ 1 .x)) [1 0])
=
DUST Result
1.0
#err <division integer zero>
2.4.1.2. reduce ↑
Function

(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.

DUST
(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])
=
DUST Result
6
4
abc
9007199254740995
DUST
(reduce (fn [acc x] (/ 1 0)) seed [])
(reduce :+ false null)
(reduce :+ null [])
=
DUST Result
seed
false
null
DUST
(reduce + 0 [])
=
DUST Result
#err <expected function>
DUST
(reduce :+ 0 abc)
=
DUST Result
#err <mismatch type>
DUST
(reduce :+ 0)
=
DUST Result
#err <arity mismatch>
DUST
(reduce (fn [acc x] (/ .acc .x)) 12.0 [2.0 0.0 3.0])
=
DUST Result
#err <division float zero>
DUST
(reduce (fn [x] .x) 0 [1])
=
DUST Result
#err <arity mismatch>
DUST
(reduce + (/ 1 0) [])
=
DUST Result
#err <expected function>
2.4.1.3. patch ↑
Function

(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:

  • null removes 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.

DUST
(let [m {a 1}] [(patch .m {a 2}) .m])
(patch {} {a {b 1}} {a {c 2}})
(patch {a 1} {a null})
=
DUST Result
[[{a 2} {a 2}] {a {b 1 c 2}} {}]
DUST
(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}})
=
DUST Result
{items [x null]
 keep 1}
{a {b 2}}
{a {c 2}}
2.4.1.4. count ↑
Function

(count collection) → integer

Returns the number of list elements, pointer segments, map members, or Unicode string code points. For null, returns zero.

DUST
(count [a b])
(count {a 1 b 2})
(count null)
=

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.

DUST
(count é猫)
(count 🙂)
(count é)
(count "")
=
DUST Result
2
1
2
0
2.4.1.5. empty? ↑
Function

(empty? collection) → boolean

Returns whether count is zero. Lists, pointers, maps, strings, and null are accepted.

DUST
(empty? [])
(empty? {})
(empty? null)
(empty? x)
=
DUST Result
true
true
true
false
2.4.1.6. first ↑
Function

(first list) → value

Returns the first element or pointer segment, or null when empty.

DUST
(first [a b])
(first [])
(first [false null])
=
DUST Result
a
null
false
2.4.1.7. rest ↑
Function

(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.

DUST
(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])
=
DUST Result
[[b c] [] [] [] [null false] [[b c] {name Ada}] [[b c] [a b c]]]
DUST
{boolean (rest false)
 extra (rest [a b] 1)
 map (rest {a 1 b 2})
 missing (rest)
 number (rest 2)
 string (rest abc)}
→
Evaluation cases
#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}]
2.4.1.8. jsonptr ↑
Function

(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>.

2.4.1.9. get ↑
Function

(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.

DUST
(get {name Ada} name)
(get {settings {theme dark}} (jsonptr "/settings/theme"))
(get {users [{name Ada} {name Bea}]} [users 1 name])
(get {} theme default)
=
DUST Result
Ada
dark
Bea
default
2.4.1.10. put ↑
Function

(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.

DUST
(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)
=
DUST Result
[[{name Bea} {name Bea}] {settings {theme dark}} {users [{name Bea}]}]
DUST
(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])
→
Canonical DUST
(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])
=
DUST Result
[[{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:

DUST
(let [original {profile {name Ada} settings {theme dark}}
      renamed (put (collections/copy .original shallow) profile {name Bea})]
  (put .renamed [settings theme] light)
  [.original .renamed])
→
Canonical DUST
(let
  [original
   {profile {name Ada}
    settings {theme dark}}
   renamed
   (put (collections/copy .original shallow) profile {name Bea})]
  (put .renamed [settings theme] light)
  [.original .renamed])
=
DUST Result
{profile {name Ada}
 settings {theme light}}
{profile {name Bea}
 settings {theme light}}
2.4.1.11. remove ↑
Function

(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.

DUST
(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])
=
DUST Result
[[{} {}] [{} {}] [{a {}} {a {}}]]
DUST
(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])
→
Canonical DUST
(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])
=
DUST Result
[[{a 1 b 2} {b 2}] [{a {b 1 c 2}} {a {c 2}}] [{a {c 2}} {a {c 2}}] [[Ada Bea] [Bea]]]
2.4.1.12. contains? ↑
Function

(contains? root path) → boolean

Returns true if path selects a value in root, including null or false, otherwise false.

DUST
(contains? {x null} x)
(contains?
  {<!x>}
  x)
(contains? {} x)
→
Canonical DUST
(contains? {x null} x)
(contains? <!x> x)
(contains? {} x)
=
DUST Result
true
true
false
DUST
(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)
=
DUST Result
true
true
true
true
false
false
false
true
true
true
true
true
true
true
true
false
DUST
{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])}
→
Evaluation cases
#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}]
2.4.1.13. keys ↑
Function

(keys map) → list

Returns the map's string keys in ascending Unicode order.

DUST
(keys {a 2 z 1})
(keys {})
(keys null)
=
DUST Result
[[a z] [] []]
2.4.1.14. values ↑
Function

(values map) → list

Returns map values in the same ascending key order used by keys.

DUST
(values {a 2 z 1})
(values {})
(values
  {<!b>
   a null})
=
DUST Result
[[2 1] [] [null false]]
2.4.1.15. pairs ↑
Function

(pairs map) → list

Returns [key value] pairs in the same ascending key order used by keys.

DUST
(pairs {a 2 z 1})
(pairs {})
(pairs {a null})
(pairs null)
(pairs
  {<!"">
   /a 1
   a/b 2})
=
DUST Result
[[[a 2] [z 1]] [] [[a null]] [] [["" false] ["/a" 1] [a/b 2]]]
DUST
{extra (pairs {} {})
 list (pairs [a 1])
 missing (pairs)
 string (pairs abc)}
→
Evaluation cases
#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.

DUST
(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]]))
=
DUST Result
{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]]
DUST
{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])}
→
Evaluation cases
#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 ↑

2.5.1.1. compare ↑
Function

(compare first second) → integer

Returns -1, 0, or 1 when first sorts before, ties with, or sorts after second, using sort ordering.

DUST
(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)
→
Canonical DUST
(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)
=
DUST Result
-1
0
1
-1
1
-1
1
0
-1
0
1
-1
DUST
{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)}
→
Evaluation cases
#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}]
2.5.1.2. sort ↑
Function

(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.

DUST
(sort [3 1 2])
(sort [猫 é a])
(sort [false true false])
(sort [#link 2 #link 1])
(sort [null null])
=
DUST Result
[[1 2 3] [a é 猫] [false false true] [#link 1 #link 2] [null null]]
DUST
(let [xs [3 1 2]] [(sort .xs) .xs])
(let [xs []] [(sort .xs) .xs])
(let [xs [2 1 2]] [(sort .xs) .xs])
=
DUST Result
[[[1 2 3] [1 2 3]] [[] []] [[1 2 2] [1 2 2]]]
DUST
(sort [1 1.0])
=
DUST Result
#err <mismatch type>
DUST
(sort [[1]])
=
DUST Result
#err <mismatch type>
DUST
(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}])
=
DUST Result
[[3 2 1] [{id b rank 1} {id a rank 2} {id c rank 2}]]
2.5.1.3. distinct ↑
Function

(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.

DUST
(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])))
=
DUST Result
[[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.

2.5.2.1. < ↑
Function

(< first second more*) → boolean

Returns whether the arguments are strictly increasing.

DUST
(< 1 2 3)
(< 1 1)
(< a é 猫)
(< false true)
=
DUST Result
true
false
true
true
DUST
(< 1 1.0)
=
DUST Result
#err <mismatch type>
DUST
(< 2 1 x)
=
DUST Result
#err <mismatch type>
DUST
(< [1] [2])
=
DUST Result
#err <mismatch type>
DUST
(< 1)
=
DUST Result
#err <arity mismatch>
2.5.2.2. <= ↑
Function

(<= first second more*) → boolean

Returns whether the arguments are nondecreasing.

DUST
(<= 1 1 2)
(<= 2 1 0)
(<= null null)
=
DUST Result
true
false
true
2.5.2.3. > ↑
Function

(> first second more*) → boolean

Returns whether the arguments are strictly decreasing.

DUST
(> 3 2 1)
(> 1 1)
(> #link 2 #link 1)
=
DUST Result
true
false
true
2.5.2.4. >= ↑
Function

(>= first second more*) → boolean

Returns whether the arguments are nonincreasing.

DUST
(>= 3 3 2)
(>= 1 2)
(>= true false)
=
DUST Result
true
false
true

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 ↑

2.6.1.1. error? ↑
Function

(error? value) → boolean

Returns true if value is an error and false otherwise. Errors can be passed as arguments without losing their tags.

DUST
[(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)]
→
Canonical DUST
(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)
=
DUST Result
true
true
true
true
true
false
false
false
false
false
false
false
false
false
false
false
false
false
DUST
(error?)
=
DUST Result
#err <arity mismatch>
DUST
(error? 1 2)
=
DUST Result
#err <arity mismatch>
2.6.1.2. null? ↑
Function

(null? x) → boolean

Returns true if x is null, otherwise false.

DUST
[(null? null)
 (null? nil)
 (null? "null")
 (null? false)]
→
Canonical DUST
(null? null)
(null? nil)
(null? "null")
(null? false)
=
DUST Result
true
false
false
false
2.6.1.3. string? ↑
Function

(string? s) → boolean

Returns true if s is a string, otherwise false.

DUST
[(string? nil)
 (string? "")
 (string? "42")
 (string? null)
 (string? 42)]
→
Canonical DUST
(string? nil)
(string? "")
(string? "42")
(string? null)
(string? 42)
=
DUST Result
true
true
true
false
false
2.6.1.4. int? ↑
Function

(int? n) → boolean

Returns true if n is an integer, otherwise false. A link scalar is not an integer.

DUST
[(int? 0)
 (int? -42)
 (int? 1.0)
 (int? 1e0)
 (int? "42")
 (int? (+ 1 2))]
→
Canonical DUST
(int? 0)
(int? -42)
(int? 1.0)
(int? 1.0)
(int? "42")
(int? (+ 1 2))
=
DUST Result
true
true
false
false
false
true
2.6.1.5. float? ↑
Function

(float? n) → boolean

Returns true if n is a floating-point number, otherwise false.

DUST
[(float? 1.0)
 (float? 1e0)
 (float? 1)
 (float? "1.0")]
→
Canonical DUST
(float? 1.0)
(float? 1.0)
(float? 1)
(float? "1.0")
=
DUST Result
true
true
false
false
2.6.1.6. number? ↑
Function

(number? n) → boolean

Returns true if n is an integer or a float, otherwise false. A link scalar is not a number.

DUST
[(number? 1)
 (number? 1.5)
 (number? "1")
 (number? true)
 (number? null)]
→
Canonical DUST
(number? 1)
(number? 1.5)
(number? "1")
(number? true)
(number? null)
=
DUST Result
true
true
false
false
false
2.6.1.7. jsonptr? ↑
Function

(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
2.6.1.8. list? ↑
Function

(list? xs) → boolean

Returns true if xs is a list, otherwise false.

DUST
[(list? [])
 (list? [1])
 (list? {})
 (list? null)]
→
Canonical DUST
(list? [])
(list? [1])
(list? {})
(list? null)
=
DUST Result
true
true
false
false
2.6.1.9. map? ↑
Function

(map? m) → boolean

Returns true if m is a map, otherwise false.

DUST
[(map? {})
 (map? {name Ada})
 (map? [])
 (map? null)]
→
Canonical DUST
(map? {})
(map? {name Ada})
(map? [])
(map? null)
=
DUST Result
true
true
false
false
2.6.1.10. boolean? ↑
Function

(boolean? b) → boolean

Returns true if b is either true or false, otherwise false.

DUST
[(boolean? true)
 (boolean? false)
 (boolean? null)
 (boolean? "true")
 (boolean? 0)]
→
Canonical DUST
(boolean? true)
(boolean? false)
(boolean? null)
(boolean? "true")
(boolean? 0)
=
DUST Result
true
true
false
false
false
2.6.1.11. template? ↑
Function

(template? value) → boolean

Returns true if value is a string template, otherwise false.

DUST
(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]]]]
→
Canonical DUST
(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""
=
DUST Result
[[true true false {strings [hello- ""] values [Ada]} hello-Ada true true 9 hello-Ada! 0 42]
 hello-&lt;Ada&gt; hello-Ada
 outer-inner-Ada]
2.6.1.12. fn? ↑
Function

(fn? f) → boolean

Returns true if f is a function, otherwise false.

DUST
[(fn? (fn [] null))
 (fn? :+)
 (fn? +)
 (fn? [])]
→
Canonical DUST
(fn? (fn [] null))
(fn? :+)
(fn? +)
(fn? [])
=
DUST Result
true
true
false
false
2.6.1.13. local? ↑
Function

(local? v) → boolean

Returns true if v is a local reference, such as the quoted form `.name, otherwise false.

DUST
[(local? `.name)
 (local? `:name)
 (local? ".name")
 (local? name)]
→
Canonical DUST
(local? `.name)
(local? `:name)
(local? ".name")
(local? name)
=
DUST Result
true
false
false
false
2.6.1.14. const? ↑
Function

(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.

DUST
[(const? `:math/pi)
 (const? `.math/pi)
 (const? :math/pi)
 (const? ":math/pi")]
→
Canonical DUST
(const? `:math/pi)
(const? `.math/pi)
(const? :math/pi)
(const? ":math/pi")
=
DUST Result
true
false
false
false

2.6.2. Type Conversion ↑

2.6.2.1. int ↑
Function

(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.

DUST
(int "9007199254740993")
(int -1.9)
(int +00042)
(int "-9223372036854775808")
=
DUST Result
9007199254740993
-1
42
-9223372036854775808
DUST
(int "9223372036854775808")
=
DUST Result
#err <integer overflow>
DUST
(int "1.5")
=
DUST Result
#err <invalid value>
DUST
(int #link 1)
=
DUST Result
#err <mismatch type>
2.6.2.2. float ↑
Function

(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.

DUST
(float 3)
(float +.5)
(float 1.e+2)
(float "1e-999")
=
DUST Result
3.0
0.5
100.0
0.0
DUST
(float "1e999")
=
DUST Result
#err <float overflow>
DUST
(float " 1")
=
DUST Result
#err <invalid value>
DUST
(float #link 1)
=
DUST Result
#err <mismatch type>
2.6.2.3. str ↑
Function

(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"
DUST
(do [
  (str [null "null" 1 true])
  (str {z 2 a 1})
  (str `.name)
  (str #utc 2026-10-24T12:34:56Z)
])
→
Canonical DUST
(do [(str [null "null" 1 true]) (str {a 1 z 2}) (str `.name) (str #utc 2026-10-24T12:34:56Z)])
=
DUST Result
""[null "null" 1 true]""
"""
  a 1
  z 2
"""
"#local name"
"#utc 2026-10-24T12:34:56Z"
2.6.2.4. boolean ↑
Function

(boolean value) → boolean

Returns false if value is null or false, otherwise true.

DUST
(boolean "")
=
DUST
(boolean 0)
=
DUST
(boolean {})
=
DUST
(boolean [])
=
2.6.2.5. format ↑
Function

(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.

DUST
(format "Hello, %s!" Ada)
(format "Invoice %04d: %.2f EUR" 7 12.5)
(format "Progress: %d%%" 75)
(format "Result: %v" {count 3})
=
DUST Result
"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.

2.7.1.1. + ↑
Function

(+) → 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.

DUST
[(+) (+ 7) (+ 1 2 3)
 (+ 1.0) (+ 1.0 2.5) (+ -2 2)]
→
Canonical DUST
(+)
(+ 7)
(+ 1 2 3)
(+ 1.0)
(+ 1.0 2.5)
(+ -2 2)
=
DUST Result
0
7
6
1.0
3.5
0
DUST
(+ 9223372036854775807 1)
=
DUST Result
#err <integer overflow>
DUST
(+ 1e+308 1e+308)
=
DUST Result
#err <float overflow>
DUST
(+ 1 "2")
=
DUST Result
#err <mismatch type>
DUST
(+ 1 2.5)
=
DUST Result
#err <mismatch type>
2.7.1.2. - ↑
Function

(- 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.

DUST
(- 3)
(- 10 3 2)
(- 10.0 2.5)
(- -9007199254740993)
(- 9007199254740995 1 1)
(- 0)
=
DUST Result
-3
5
7.5
9007199254740993
9007199254740993
0
DUST
(-)
=
DUST Result
#err <argument missing>
DUST
(- -9223372036854775808)
=
DUST Result
#err <integer overflow>
DUST
(- -9223372036854775808 1)
=
DUST Result
#err <integer overflow>
DUST
(- 10 2.5)
=
DUST Result
#err <mismatch type>
DUST
(- -1e+308 1e+308)
=
DUST Result
#err <float overflow>
DUST
(- 1 x)
=
DUST Result
#err <mismatch type>
DUST
(- #link 1)
=
DUST Result
#err <mismatch type>
2.7.1.3. * ↑
Function

(*) → 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.

DUST
[(*) (* 7) (* 2 3 4)
 (* 1.0) (* 2.0 1.5) (* -2 0)]
→
Canonical DUST
(*)
(* 7)
(* 2 3 4)
(* 1.0)
(* 2.0 1.5)
(* -2 0)
=
DUST Result
1
7
24
1.0
3.0
0
DUST
(* 9223372036854775807 2)
=
DUST Result
#err <integer overflow>
DUST
(* 1e+308 2.0)
=
DUST Result
#err <float overflow>
DUST
(* null)
=
DUST Result
#err <mismatch type>
DUST
(* 2 1.5)
=
DUST Result
#err <mismatch type>
2.7.1.4. / ↑
Function

(/ 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.

DUST
[(/ 2) (/ 6 3) (/ 1 2) (/ 24 2 3) (/ -2)
 (/ 1 3) (/ 1.0 2.0) (/ 0 2)]
→
Canonical DUST
(/ 2)
(/ 6 3)
(/ 1 2)
(/ 24 2 3)
(/ -2)
(/ 1 3)
(/ 1.0 2.0)
(/ 0 2)
=
DUST Result
0.5
2.0
0.5
4.0
-0.5
0.3333333333333333
0.5
0.0
DUST
(/ 1.0 2)
=
DUST Result
#err <mismatch type>
DUST
(/)
=
DUST Result
#err <argument missing>
DUST
(/ 1 0)
=
DUST Result
#err <division integer zero>
DUST
(/ 0.0)
=
DUST Result
#err <division float zero>
DUST
(/ 1e+308 0.5)
=
DUST Result
#err <float overflow>
DUST
(/ 1 "2")
=
DUST Result
#err <mismatch type>
2.7.1.5. rem ↑
Function

(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.

DUST
(rem -7 3)
(rem 7 -3)
(rem 1.25 0.5)
(rem -9223372036854775808 -1)
=
DUST Result
-1
1
0.25
0
DUST
(rem 1 0)
=
DUST Result
#err <division integer 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.

DUST
(comparison/not= 1 2)
(comparison/not= 1 1)
(comparison/not= 1)
(comparison/not= 1 2 x)
=
DUST Result
true
false
false
true
DUST
(comparison/not= 1 1.0)
=
DUST Result
#err <mismatch type>
DUST
(comparison/not=)
=
DUST Result
#err <argument missing>

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.

DUST
(comparison/identical? 1 1.0)
=
DUST
(comparison/identical? hello "hello")
→
Canonical DUST
(comparison/identical? hello hello)
=
DUST
(comparison/identical? [1 2] [1 2])
=
DUST
(comparison/identical? {x 1} {x 1})
=
DUST
(comparison/identical? `[] [])
=
DUST
(comparison/identical? `[1 2] [1 2])
=
DUST
(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])
→
Canonical DUST
(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])
=
DUST Result
true
true
false
false
true
[#err {<division integer zero>
       message "division by zero"}
 #err {<division integer zero>
       message "division by zero"}]
DUST
(let [a {x 1}
      b {x 1}
      c .a]
  (comparison/identical? .a .b))
→
Canonical DUST
(let [a {x 1} b {x 1} c .a] (comparison/identical? .a .b))
=
DUST
(let [a {x 1}
      b {x 1}
      c .a]
  (comparison/identical? .a .c))
→
Canonical DUST
(let [a {x 1} b {x 1} c .a] (comparison/identical? .a .c))
=
DUST
(let [f (fn [x] .x)
      g .f
      h (fn [x] .x)]
  (comparison/identical? .f .g))
→
Canonical DUST
(let [f (fn [x] .x) g .f h (fn [x] .x)] (comparison/identical? .f .g))
=
DUST
(let [f (fn [x] .x)
      g .f
      h (fn [x] .x)]
  (comparison/identical? .f .h))
→
Canonical DUST
(let [f (fn [x] .x) g .f h (fn [x] .x)] (comparison/identical? .f .h))
=

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.

DUST
((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)
=
DUST Result
13
7
6
3
6
21
[null false x]
true
DUST
{function ((fn/partial 1 a) b)
 missing (fn/partial)
 too-few ((fn/partial :fn/identity))
 too-many ((fn/partial :fn/identity a) b)}
→
Evaluation cases
#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}]

4.1.2. fn/comp ↑

Function

(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.

DUST
((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 :+) :+)
=
DUST Result
hello
2
8
[a b]
6
0
20
6
true
true
DUST
{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)}
→
Evaluation cases
#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.

DUST
(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))
=
DUST Result
null
false
[a b]
{a 1}
0
hello
true
true
DUST
{extra (fn/identity a b)
 missing (fn/identity)}
→
Evaluation cases
#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.

DUST
((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)])
=
DUST Result
a
false
null
[a b]
true
[x x x]
DUST
{extra (fn/constantly a b)
 missing (fn/constantly)}
→
Evaluation cases
#cases [{output #err <arity mismatch> select missing} {output #err <arity mismatch> select extra}]

4.1.5. fn/juxt ↑

Function

(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.

DUST
((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)
=
DUST Result
[[a c] [2 x] [null null] [false] [0 1] [10 6 16] [10 24 10 24] [null false]]
DUST
(fn/juxt)
=
DUST Result
#err <arity mismatch>

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.

DUST
((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)
=
DUST Result
false
true
true
false
true
false
false
false
true
false
true
DUST
{extra (fn/complement :fn/identity :fn/identity)
 missing (fn/complement)}
→
Evaluation cases
#cases [{output #err <arity mismatch> select missing} {output #err <arity mismatch> select extra}]

4.1.7. fn/fnull ↑

Function

(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.

DUST
((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)
=
DUST Result
3
0
false
[x y z]
[x null false]
[x y]
null
[x false z null]
DUST
{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)}
→
Evaluation cases
#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.

5.1. true? ↑

Function

(true? value) → boolean

Returns true if value is the boolean true, otherwise false.

DUST
(true? true)
=
DUST
(true? false)
=
DUST
(true? a)
=
DUST
(true? (boolean a))
=

5.2. false? ↑

Function

(false? value) → boolean

Returns true if value is the boolean false, otherwise false.

DUST
(false? true)
=
DUST
(false? false)
=
DUST
(false? nil)
=
DUST
(false? (boolean null))
=
DUST
(false? (boolean nil))
=

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.

DUST
(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]])
=
DUST Result
[[a a b b] [] [1 2] [] [] [1 a] [1 a false 2 b null] [[a] [b]] [null false 0]]
DUST
{list (lists/mapcat :fn/identity abc)
 missing (lists/mapcat)
 result (lists/mapcat :fn/identity [1])
 transducer (lists/mapcat :fn/identity)}
→
Evaluation cases
#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.

DUST
(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])
=
DUST Result
[[1 false 2] [false 1] [] [] [] [false 0 "" [] {}] [2 4 6] [[a] [b]]]
DUST
{extra (lists/keep :fn/identity [a] [b])
 list (lists/keep :fn/identity abc)
 missing (lists/keep)
 transducer (lists/keep :fn/identity)}
→
Evaluation cases
#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}]

6.1.3. lists/nth ↑

Function

(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.

DUST
(lists/nth [a b c] 0)
(lists/nth [a b c] 2)
(lists/nth [] 0 missing)
=
DUST Result
a
c
missing
DUST
(lists/nth [] 0)
=
DUST Result
#err <invalid value>

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.

DUST
(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]])
=
DUST Result
b
null
null
null
false
null
{name Ada}
[b c]
DUST
{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)}
→
Evaluation cases
#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.

DUST
(lists/last [a b])
(lists/last [])
(lists/last [false null])
=
DUST Result
b
null
null

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.

DUST
(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])
=
DUST Result
[[[a b] [a b]] [[null false] [null false]] [[1 2 3 4] [1 2 3 4]]]
DUST
(lists/append [1 2] 3 4)
(lists/append [] null false)
(let [xs [a]] [(lists/append (collections/copy .xs) b) .xs])
=
DUST Result
[[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.

DUST
(lists/concat [1] [2 3])
(lists/concat)
(lists/concat null [a] [])
=
DUST Result
[[1 2 3] [] [a]]

6.1.8. lists/interpose ↑

Function

(lists/interpose separator list) → list

Returns a fresh list with separator between adjacent elements of list.

DUST
(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])
=
DUST Result
[[a x b x c] [a] [] [] [a null b] [a false b] [[a] [x y] [b]] [[a x b] [a b]]]
DUST
{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)}
→
Evaluation cases
#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.

DUST
(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])
=
DUST Result
[[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]]]
DUST
{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)}
→
Evaluation cases
#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>.

DUST
(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])
=
DUST Result
[[1 2 3 4]
 [1 null 2]
 []
 []
 [1 2 3]
 [a a false true]
 [
   {items [1 [2]]}
   hello world
   #custom [3 [4]]
   null]]
DUST
{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])}
→
Evaluation cases
#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.

DUST
(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)
=
DUST Result
[[b c] [b c] [] [a b]]
DUST
(lists/slice [a] -1)
=
DUST Result
#err <invalid value>

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.

DUST
(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])
=
DUST Result
[[a b] [a b] [] [] [] [] [a b] [] [null false] [[a b] [a b c]]]
DUST
{count (lists/take two [a b])
 extra (lists/take 1 [a] [b])
 list (lists/take 1 abc)
 missing (lists/take)
 transducer (lists/take 1)}
→
Evaluation cases
#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.

DUST
(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])
=
DUST Result
[[1 2] [1] [] [] [] [0 "" []] [null false] [1 2]]
DUST
{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)}
→
Evaluation cases
#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.

DUST
(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])
=
DUST Result
[[c d] [] [a b] [] [a b] [] [c] [a b] [null false] [[b c] [a b c]]]
DUST
{count (lists/drop two [a b])
 extra (lists/drop 1 [a] [b])
 list (lists/drop 1 abc)
 missing (lists/drop)
 transducer (lists/drop 1)}
→
Evaluation cases
#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.

DUST
(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])
=
DUST Result
[[3 1] [false 2] [] [] [false 1] [null 1] [] [3 invalid]]
DUST
{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)}
→
Evaluation cases
#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.

DUST
(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])
=
DUST Result
[[[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]]]
DUST
{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])}
→
Evaluation cases
#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.

DUST
(let [xs [a b c]] [(lists/reverse .xs) .xs])
(let [xs []] [(lists/reverse .xs) .xs])
(let [xs [a]] [(lists/reverse .xs) .xs])
=
DUST Result
[[[c b a] [c b a]] [[] []] [[a] [a]]]
DUST
(lists/reverse [a b c])
(lists/reverse [])
(lists/reverse [null false])
=
DUST Result
[[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.

DUST
(lists/filter :boolean [null false 0 a])
(lists/filter (fn [x] (= .x 2)) [1 2 2 3])
(lists/filter :boolean [])
=
DUST Result
[[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.

DUST
(lists/any? :boolean [null false a])
(lists/any? :boolean [])
(lists/any? (fn [x] (if .x true (/ 1 0))) [true false])
=
DUST Result
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.

DUST
(lists/every? :boolean [0 a])
(lists/every? :boolean [])
(lists/every? (fn [x] (if .x (/ 1 0) false)) [false true])
=
DUST Result
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.

DUST
(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])
=
DUST Result
Ada
0
null
null
null
[]
""
{found 2}
yes
DUST
{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)}
→
Evaluation cases
#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.

DUST
(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)
=
DUST Result
{name Ada}
false
null
fallback
null
fallback
0
null
false
DUST
{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)}
→
Evaluation cases
#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.

DUST
(lists/range 3)
(lists/range 1 6 2)
(lists/range 3 0 -1)
(lists/range 3 0)
=
DUST Result
[[0 1 2] [1 3 5] [3 2 1] []]
DUST
(lists/range 0 3 0)
=
DUST Result
#err <invalid value>

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.

DUST
(lists/repeat 3 x)
(lists/repeat 0 x)
(lists/repeat 2 null)
=
DUST Result
[[x x x] [] [null null]]
DUST
(lists/repeat -1 x)
=
DUST Result
#err <invalid value>

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.

DUST
(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)])
→
Canonical DUST
(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)])
=
DUST Result
[[[a 2] [b 1] [b 3]] [a ab abc] [] [[1 2 3] [3 2 1]]]
DUST
(lists/sort-by :first [[1 a] [1.0 b]])
=
DUST Result
#err <mismatch type>
DUST
(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])
=
DUST Result
[[[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.

DUST
(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])
=
DUST Result
[[[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]]]
DUST
{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)}
→
Evaluation cases
#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.

DUST
(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 [])
=
DUST Result
{a [[a 1] [a 3]]
 b [[b 2]]}
{A [a A]
 B [b]}
{}
DUST
(lists/group-by :first [[1 a]])
=
DUST Result
#err <mismatch type>

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".

DUST
(lists/frequencies [a b a])
(lists/frequencies [])
(lists/frequencies [é 猫 é])
=
DUST Result
{a 2
 b 1}
{}
{é 2
 猫 1}
DUST
(lists/frequencies [1 "1"])
=
DUST Result
#err <mismatch type>

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.

DUST
(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])
=
DUST Result
{a 1
 b 2}
{a 1}
{a 2}
{}
{a 1}
{}
{}
{}
{<!b>
 a null
 c [1 2]}
{a null}
{"" 1
 /a 2
 a/b 3}
DUST
{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)}
→
Evaluation cases
#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.

DUST
(maps/select-keys {a 1 b 2} [b missing])
(maps/select-keys {a null} [a a])
(maps/select-keys null [a])
=
DUST Result
{b 2}
{a null}
{}
DUST
(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)
=
DUST Result
{a 1
 c 3}
{/a 1}
{a 1}
{/a/b 2}
{<!b>
 a null}
{}
{}
{}
DUST
{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]])}
→
Evaluation cases
#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>.

DUST
(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)
=
DUST Result
{score 5}
{user {score 5}}
{tags [old new]}
DUST
(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])
=
DUST Result
[[{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>.

DUST
(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)]))
→
Canonical DUST
(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)]))
=
DUST Result
[[{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.

DUST
#hash #sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
→
RON
#hash #sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
=
DUST Result
#hash #sha256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
DUST
#hash #md5 900150983cd24fb0d6963f7d28e17f72
=
DUST Result
#err <invalid tag>
DUST
#hash #sha256 ba7816bf
=
DUST Result
#err <invalid tag>
DUST
#hash #xxh3-64 78AF5F94892F3950
=
DUST Result
#err <invalid tag>

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 of 1 and 2 on 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 1 and float 1.0 have different forms and digests.
  • #utc and #dur payloads 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 fn source, 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>.

DUST
(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)
=
DUST Result
#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
DUST
(hash/digest sha512 abc)
(hash/digest sha3-256 abc)
(hash/digest sha3-512 abc)
(hash/digest xxh3-64 abc)
(hash/digest xxh3-128 abc)
=
DUST Result
#hash #sha512 ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f
#hash #sha3-256 3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532
#hash #sha3-512 b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0
#hash #xxh3-64 78af5f94892f3950
#hash #xxh3-128 06b05ab6733a618578af5f94892f3950
DUST
(= (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 é))
=
DUST Result
true
true
true
DUST
(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)
=
DUST Result
true
true
true
DUST
(hash/digest md5 abc)
=
DUST Result
#err <algorithm hash unknown>
DUST
(hash/digest 256 abc)
=
DUST Result
#err <mismatch type>
DUST
(hash/digest sha256 (fn [] 1))
=
DUST Result
#hash #sha256 06d0dff99f70155bfd64b168de5c36cede11e9c14ddf87358cbb89ea9f08ce69
DUST
(hash/digest sha256 [(fn [] 1)])
=
DUST Result
#hash #sha256 b6ca49a321d2250ae8533a020a3fbf07d9942b3af2e67728b342ed4b73f86d0e
DUST
(hash/digest)
=
DUST Result
#err <arity mismatch>

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.

DUST
(let [g (random/seeded 42)]
  [(random/int .g 0 6) (random/int .g 0 6) .g])
→
Canonical DUST
(let [g (random/seeded 42)] [(random/int .g 0 6) (random/int .g 0 6) .g])
=
DUST Result
0
2
#rng {xoshiro256ss 9d1c68b67d1ceb4323acb714c3a78d019d431c524cad8dc572e82a070b0b9ddb}
DUST
(let [g #rng {xoshiro256ss "9d1c68b67d1ceb4323acb714c3a78d019d431c524cad8dc572e82a070b0b9ddb"}]
  [(random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) .g])
→
Canonical DUST
(let
  [g #rng {xoshiro256ss 9d1c68b67d1ceb4323acb714c3a78d019d431c524cad8dc572e82a070b0b9ddb}]
  [(random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) .g])
=
DUST Result
4
5
5
#rng {xoshiro256ss 7e3fedbea92a13a5c9a25ba0f11c828cc38346747039f414cf55c271f2386fa5}
DUST
(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)
→
Canonical DUST
(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)
=
DUST Result
#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>
DUST
(random/seeded 42)
(random/seeded 0)
(random/seeded -1)
(random/seeded -9223372036854775808)
=
DUST Result
#rng {xoshiro256ss bdd732262feb6e9528efe333b266f10347526757130f9f52581ce1ff0e4ae394}
#rng {xoshiro256ss e220a8397b1dcdaf6e789e6aa1b965f406c45d188009454ff88bb8a8724c81ec}
#rng {xoshiro256ss e4d971771b652c20e99ff867dbf682c9382ff84cb27281e96d1db36ccba982d2}
#rng {xoshiro256ss 481ec0a212a9f3dbc46fa638a630901261a685ffc80a8140592e268383e356f9}
DUST
(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)])
→
Canonical DUST
(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)])
=
DUST Result
70
70
27
27
DUST
(random/seeded 1.5)
(random/seeded abc)
(random/seeded)
(random/int 42 0 6)
(random/int #other {xoshiro256ss "6db07c7dd404690b81ddc5fe6c15769825cfe223490d9d1f9252543d113b0c36"} 0 6)
→
Canonical DUST
(random/seeded 1.5)
(random/seeded abc)
(random/seeded)
(random/int 42 0 6)
(random/int
  #other {xoshiro256ss 6db07c7dd404690b81ddc5fe6c15769825cfe223490d9d1f9252543d113b0c36}
  0
  6)
=
DUST Result
#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>
DUST
(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)])
→
Canonical DUST
(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)])
=
DUST Result
[[0 2 4 5 5 4 4 5 4 3] [-10 5 360086822056278777] [2 0 2 2 2]]
DUST
(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)])
→
Canonical DUST
(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)])
=
DUST Result
[[-7676373272452217067 -2232420343890232707 3321214725393783200]
 [-832274057880235478 -242039498674662721 360086822056278777]]
DUST
(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])
→
Canonical DUST
(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])
=
DUST Result
[[0 #rng {xoshiro256ss 0000000004000080000020000400000040000000040000000010000004000000}]
 [0 #rng {xoshiro256ss 0000000004000080000020000400000040000000040000000010000004000000}]]
DUST
(random/int (random/seeded 1) 5 5)
(random/int (random/seeded 1) 6 0)
(random/int (random/seeded 1) 9223372036854775807 -9223372036854775808)
=
DUST Result
#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"}
DUST
(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)
=
DUST Result
#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>
DUST
(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)])
→
Canonical DUST
(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)])
=
DUST Result
[[0.08386297105988216 0.3789802506626686 0.6800434110281394]
 [-2.080685144700589 100.18949012533133 3.600868220562787e+307]
 [7.0057648217968965e-301 -0.7212487705262157]]
DUST
(random/float (random/seeded 1) 1.0 1.0)
(random/float (random/seeded 1) 2.0 1.0)
=
DUST Result
#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"}
DUST
(random/float (random/seeded 1) 0 1)
(random/float (random/seeded 1) 0.0 1)
(random/float (random/seeded 1) a b)
=
DUST Result
#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>
DUST
(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]]))
→
Canonical DUST
(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]]))
=
DUST Result
[[a b c c] [only 2.5] [3 4]]
DUST
(random/choose (random/seeded 1) [])
(random/choose (random/seeded 1) null)
=
DUST Result
#err {<invalid value>
      message "random/choose requires a nonempty list"}
#err {<invalid value>
      message "random/choose requires a nonempty list"}
DUST
(random/choose (random/seeded 1) {a 1})
(random/choose (random/seeded 1) abc)
(random/choose [a b])
=
DUST Result
#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>
DUST
(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)])
→
Canonical DUST
(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)])
=
DUST Result
[[[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] []]]
DUST
(random/shuffle (random/seeded 1) {a 1})
(random/shuffle (random/seeded 1) abc)
(random/shuffle [1 2 3])
=
DUST Result
#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.

DUST
(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)
=
DUST Result
STRASSE
hello
httpServer
"Hello World"
strasse
DUST
(strings/case [snake upper] HTTPServer)
(strings/case [upper lower] Straße)
(strings/case [] Hello)
(strings/case [camel snake] hello_world)
=
DUST Result
HTTP_SERVER
strasse
Hello
hello_world
DUST
(strings/case unknown abc)
=
DUST Result
#err <invalid value>
DUST
(strings/case 1 abc)
=
DUST Result
#err <mismatch type>

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.

DUST
(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  ")
=
DUST Result
"alpha beta"
hi
hi
""
""
​hi​
hi
DUST
(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  ")
=
DUST Result
haha
ha
""
ba
ba
xhaha
hahax
xhahax
aabb
""
ab
abc
""
""
x
Éx
éxé
" x "
x
DUST
{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)}
→
Evaluation cases
#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.

DUST
(strings/split "a,b,c" ",")
(strings/split é猫🙂 "")
(strings/split a.b. ".")
(strings/split "" ",")
(strings/split "" "")
=
DUST Result
[[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.

DUST
(strings/join [a b] ",")
(strings/join [é 猫 🙂] "")
(strings/join [] 猫)
=
DUST Result
"a,b"
é猫🙂
""

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.

DUST
(strings/replace banana na x)
(strings/replace ab "" x)
(strings/replace é猫 "" 🙂)
(strings/replace a.b. "." 猫)
=
DUST Result
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>.

DUST
(strings/substring Aé猫Z 1 3)
(strings/substring é猫🙂 1 3)
(strings/substring é猫 2 2)
(strings/substring é猫🙂 1)
=
DUST Result
é猫
猫🙂
""
猫🙂

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.

DUST
(strings/starts-with? abc a)
(strings/starts-with? é猫 é)
(strings/starts-with? é猫 猫)
(strings/starts-with? é猫 "")
=
DUST Result
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.

DUST
(strings/ends-with? abc c)
(strings/ends-with? é猫 猫)
(strings/ends-with? é猫 é)
(strings/ends-with? é猫 "")
=
DUST Result
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.

DUST
(strings/includes? a.b ".")
(strings/includes? é猫🙂 猫)
(strings/includes? é猫 z)
(strings/includes? é猫 "")
(strings/includes? é é)
=
DUST Result
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.

DUST
[(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")]
→
Canonical DUST
(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)
=
DUST Result
[[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.

DUST
[(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)]
→
Canonical DUST
(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)
=
DUST Result
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.

DUST
[(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 "" "")]
→
Canonical DUST
(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 "" "")
=
DUST Result
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.

DUST
[(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" "\\")]
→
Canonical DUST
(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 "\\")
=
DUST Result
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>.

DUST
[(strings/blank? null)
 (strings/blank? "")
 (strings/blank? " \t\r\n")
 (strings/blank? " ")
 (strings/blank? " ")
 (strings/blank? " ")
 (strings/blank? " ")
 (strings/blank? "​")
 (strings/blank? " a ")]
→
Canonical DUST
(strings/blank? null)
(strings/blank? "")
(strings/blank?
  " \t\r\n")
(strings/blank?  )
(strings/blank?  )
(strings/blank?  )
(strings/blank?  )
(strings/blank? ​)
(strings/blank? " a ")
=
DUST Result
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.

DUST
(let
  [name Ada]
  [(= ~"hello-,.name" (str hello- .name)) (template? ~"hello-,.name")
   (template? (str hello- .name))])
=
DUST Result
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>")) ; => "&lt;Alice&gt;"

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>.

DUST
[(strings/escape html-attribute "&<>\"'")
 (strings/escape html-attribute "<b>Tom & 'Sue'</b>")
 (strings/escape html-attribute "&amp;")
 (strings/escape html-attribute "\\$1🙂")
 (strings/escape html-attribute "")]
→
Canonical DUST
(strings/escape html-attribute &<>"')
(strings/escape html-attribute "<b>Tom & 'Sue'</b>")
(strings/escape html-attribute &amp;)
(strings/escape html-attribute "\\$1🙂")
(strings/escape html-attribute "")
=
DUST Result
&amp;&lt;&gt;&quot;&#39;
"&lt;b&gt;Tom &amp; &#39;Sue&#39;&lt;/b&gt;"
&amp;amp;
"\\$1🙂"
""
DUST
(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 "*~ ")
→
Canonical DUST
(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 "*~ ")
=
DUST Result
a~1b~0
a/b~
a%26b%20c
a%26b+c
"a&b c"
"""
  "a,b"
"""
"a,b"
*%7E+
DUST
(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")
→
Canonical DUST
(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")
=
DUST Result
""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"}
DUST
(strings/escape html-text "<a & b>")
(strings/escape html-attribute "\"<a>\"")
(strings/unescape html-text "&lt;a &amp; b&gt;")
(strings/unescape html-attribute "&#x1F600; &quot;x&quot;")
(strings/unescape html-text "&copy; &NotEqualTilde;")
→
Canonical DUST
(strings/escape html-text "<a & b>")
(strings/escape
  html-attribute
  """
    "<a>"
  """
)
(strings/unescape html-text "&lt;a &amp; b&gt;")
(strings/unescape html-attribute "&#x1F600; &quot;x&quot;")
(strings/unescape html-text "&copy; &NotEqualTilde;")
=
DUST Result
"&lt;a &amp; b&gt;"
&quot;&lt;a&gt;&quot;
"<a & b>"
""😀 "x"""
"© ≂̸"
DUST
(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 "&lt;Alice&gt;"))
(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)
→
Canonical DUST
(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 &lt;Alice&gt;))
(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)
=
DUST Result
a%2Fb
a/b
&lt;Alice&gt;
"<Alice>"
"<p>&lt;Alice&gt;</p>"
true
#err <invalid value>
#err <invalid value>
#err <mismatch type>
#err <mismatch type>
#err <arity mismatch>
DUST
(let [escape-html (strings/escape html-text)]
  [(.escape-html "<Alice>")
   (fn? .escape-html)
   (.escape-html ~"<,(str b),>")
   (hash/digest sha256 .escape-html)])
→
Canonical DUST
(let
  [escape-html (strings/escape html-text)]
  [(.escape-html "<Alice>") (fn? .escape-html)
   (.escape-html ~"<,(str b),>") (hash/digest sha256 .escape-html)])
=
DUST Result
&lt;Alice&gt;
true
&lt;b&gt;
#hash #sha256 2d140a5655b694b51aecece118441a80fdd245852dc87ae6babb33de709aba34
DUST
[(strings/escape)
 (strings/unescape html-text a b)
 ((strings/escape html-text))
 ((strings/unescape html-text) 1)]
→
Canonical DUST
(strings/escape)
(strings/unescape html-text a b)
((strings/escape html-text))
((strings/unescape html-text) 1)
=
DUST Result
#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 "&lt;Alice&gt;") ; => "<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.

DUST
[(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)]
→
Canonical DUST
(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)
=
DUST Result
é
é
A1ffi
Å1
①
가
""
É
ࢡ
DUST
(strings/normalize "é" nfc)
→
Canonical DUST
(strings/normalize é nfc)
=
DUST Result
#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.

DUST
[(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])]
→
Canonical DUST
(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])
=
DUST Result
Á
🙂
""
"\u0000A"
􏿿
͸
DUST
(strings/from-code-points [55296])
=
DUST Result
#err <code invalid point>

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.

DUST
(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})
=
DUST Result
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.

DUST
(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)
=
DUST Result
"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.

DUST
(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})
→
Canonical DUST
(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})
=
DUST Result
2026-10-29T12:00:00.1234567Z
"3 minutes ago"
"in 2 days"
now
DUST
(strings/humanize #utc 2026-10-29T12:00Z {mode relative})
→
Canonical DUST
(strings/humanize #utc 2026-10-29T12:00:00Z {mode relative})
=
DUST Result
#err <invalid value>
DUST
(strings/humanize #dur PT1S {scale si})
=
DUST Result
#err <invalid value>
DUST
(strings/humanize 1 {precision 10})
=
DUST Result
#err <invalid value>
DUST
(strings/humanize 1 {unknown 1})
=
DUST Result
#err <invalid value>
DUST
(strings/humanize #link 1)
=
DUST Result
#err <mismatch type>

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.

DUST
:math/pi
=
DUST Result
3.141592653589793

11.1.2. math/tau ↑

The constant :math/tau is the float 6.283185307179586, the nearest binary64 approximation to two times pi.

DUST
:math/tau
=
DUST Result
6.283185307179586

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.

DUST
:math/deg2rad
=
DUST Result
0.017453292519943295

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.

DUST
:math/rad2deg
=
DUST Result
57.29577951308232

11.1.5. math/e ↑

The constant :math/e is the float 2.718281828459045, the nearest binary64 approximation to the base of natural logarithms.

DUST
:math/e
=
DUST Result
2.718281828459045

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.

DUST
[(math/floor 1.8) (math/floor -1.2) (math/floor 2)
 (math/floor 0.0) (math/floor 9007199254740993) (math/floor 100000000000000000000.0)]
→
Canonical DUST
(math/floor 1.8)
(math/floor -1.2)
(math/floor 2)
(math/floor 0.0)
(math/floor 9007199254740993)
(math/floor 100000000000000000000.0)
=
DUST Result
1.0
-2.0
2.0
0.0
9007199254740992.0
100000000000000000000.0

11.1.7. math/ceil ↑

Function

(math/ceil n) → float

Returns the least integral-valued float greater than or equal to n after conversion to a float.

DUST
[(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)]
→
Canonical DUST
(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)
=
DUST Result
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>.

DUST
[(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)]
→
Canonical DUST
(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)
=
DUST Result
1
2
3
-2
-3
-2
1
-1
0
-1
7
9007199254740993
9223372036854775807
-9223372036854775808
-9223372036854775808
9223372036854774784
DUST
(math/round 9223372036854776000.0)
=
DUST Result
#err <integer overflow>

11.1.9. math/rint ↑

Function

(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.

DUST
[(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)]
→
Canonical DUST
(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)
=
DUST Result
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.

DUST
[(math/trunc 1.8) (math/trunc -1.8) (math/trunc 2) (math/trunc -0.2) (math/trunc 9007199254740993)]
→
Canonical DUST
(math/trunc 1.8)
(math/trunc -1.8)
(math/trunc 2)
(math/trunc -0.2)
(math/trunc 9007199254740993)
=
DUST Result
1.0
-1.0
2.0
0.0
9007199254740992.0

11.1.11. math/pow ↑

Function

(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>.

DUST
[(math/pow 2 3) (math/pow -2 3) (math/pow 9 0.5) (math/pow 0 0) (math/pow 2 -3)]
→
Canonical DUST
(math/pow 2 3)
(math/pow -2 3)
(math/pow 9 0.5)
(math/pow 0 0)
(math/pow 2 -3)
=
DUST Result
8.0
-8.0
#err <mismatch type>
1.0
0.125
DUST
(math/pow -2 0.5)
=
DUST Result
#err <mismatch type>

11.1.12. math/sqrt ↑

Function

(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.

DUST
[(math/sqrt 0) (math/sqrt 4) (math/sqrt 9.0)]
→
Canonical DUST
(math/sqrt 0)
(math/sqrt 4)
(math/sqrt 9.0)
=
DUST Result
0.0
2.0
3.0

11.1.13. math/cbrt ↑

Function

(math/cbrt n) → float

Returns the real cube root of n, with the same sign as n.

DUST
[(math/round (* 1000000000000.0 (math/cbrt -8))) (math/round (* 1000000000000.0 (math/cbrt 0)))
 (math/round (* 1000000000000.0 (math/cbrt 27)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/cbrt -8)))
(math/round (* 1000000000000.0 (math/cbrt 0)))
(math/round (* 1000000000000.0 (math/cbrt 27)))
=
DUST Result
-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>.

DUST
[(math/hypot 3 4) (math/hypot -3 -4) (math/hypot 0 0) (math/hypot 0 1.5)]
→
Canonical DUST
(math/hypot 3 4)
(math/hypot -3 -4)
(math/hypot 0 0)
(math/hypot 0 1.5)
=
DUST Result
5.0
5.0
0.0
#err <mismatch type>
DUST
[(math/round (* 1000000000000.0 (/ (math/hypot 1e+308 1e+308) 1e+308)))
 (math/round (* 1000000000000.0 (/ (math/hypot 1e-308 1e-308) 1e-308)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (/ (math/hypot 1e+308 1e+308) 1e+308)))
(math/round (* 1000000000000.0 (/ (math/hypot 1e-308 1e-308) 1e-308)))
=
DUST Result
1414213562373
1414213562373

11.1.15. math/exp ↑

Function

(math/exp n) → float

Returns e raised to n.

DUST
[(math/round (* 1000000000000.0 (math/exp -1))) (math/round (* 1000000000000.0 (math/exp 0)))
 (math/round (* 1000000000000.0 (math/exp 1)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/exp -1)))
(math/round (* 1000000000000.0 (math/exp 0)))
(math/round (* 1000000000000.0 (math/exp 1)))
=
DUST Result
367879441171
1000000000000
2718281828459

11.1.16. math/exp2 ↑

Function

(math/exp2 n) → float

Returns two raised to n.

DUST
[(math/exp2 -3) (math/exp2 0) (math/exp2 3)]
→
Canonical DUST
(math/exp2 -3)
(math/exp2 0)
(math/exp2 3)
=
DUST Result
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.

DUST
[(math/round (* 1000000000000.0 (math/expm1 -1))) (math/round (* 1000000000000.0 (math/expm1 0)))
 (math/round (* 1000000000000.0 (math/expm1 1)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/expm1 -1)))
(math/round (* 1000000000000.0 (math/expm1 0)))
(math/round (* 1000000000000.0 (math/expm1 1)))
=
DUST Result
-632120558829
0
1718281828459

11.1.18. math/log ↑

Function

(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>.

DUST
[(math/round (* 1000000000000.0 (math/log 0.5))) (math/round (* 1000000000000.0 (math/log 1)))
 (math/round (* 1000000000000.0 (math/log 2)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/log 0.5)))
(math/round (* 1000000000000.0 (math/log 1)))
(math/round (* 1000000000000.0 (math/log 2)))
=
DUST Result
-693147180560
0
693147180560

11.1.19. math/log2 ↑

Function

(math/log2 n) → float

Returns the base-two logarithm of n.

A zero or negative n produces #err <float overflow>.

DUST
[(math/log2 0.5) (math/log2 1) (math/log2 2) (math/log2 8)]
→
Canonical DUST
(math/log2 0.5)
(math/log2 1)
(math/log2 2)
(math/log2 8)
=
DUST Result
-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>.

DUST
[(math/log10 0.1) (math/log10 1) (math/log10 10) (math/log10 100)]
→
Canonical DUST
(math/log10 0.1)
(math/log10 1)
(math/log10 10)
(math/log10 100)
=
DUST Result
-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>.

DUST
[(math/round (* 1000000000000.0 (math/log1p -0.5))) (math/round (* 1000000000000.0 (math/log1p 0)))
 (math/round (* 1000000000000.0 (math/log1p 1)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/log1p -0.5)))
(math/round (* 1000000000000.0 (math/log1p 0)))
(math/round (* 1000000000000.0 (math/log1p 1)))
=
DUST Result
-693147180560
0
693147180560

11.1.22. math/sin ↑

Function

(math/sin angle) → float

Returns the sine of angle in radians.

DUST
[(math/round (* 1000000000000.0 (math/sin -1))) (math/round (* 1000000000000.0 (math/sin 0)))
 (math/round (* 1000000000000.0 (math/sin 1)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/sin -1)))
(math/round (* 1000000000000.0 (math/sin 0)))
(math/round (* 1000000000000.0 (math/sin 1)))
=
DUST Result
-841470984808
0
841470984808

11.1.23. math/cos ↑

Function

(math/cos angle) → float

Returns the cosine of angle in radians.

DUST
[(math/round (* 1000000000000.0 (math/cos -1))) (math/round (* 1000000000000.0 (math/cos 0)))
 (math/round (* 1000000000000.0 (math/cos 1)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/cos -1)))
(math/round (* 1000000000000.0 (math/cos 0)))
(math/round (* 1000000000000.0 (math/cos 1)))
=
DUST Result
540302305868
1000000000000
540302305868

11.1.24. math/tan ↑

Function

(math/tan angle) → float

Returns the tangent of angle in radians.

DUST
[(math/round (* 1000000000000.0 (math/tan -1))) (math/round (* 1000000000000.0 (math/tan 0)))
 (math/round (* 1000000000000.0 (math/tan 1)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/tan -1)))
(math/round (* 1000000000000.0 (math/tan 0)))
(math/round (* 1000000000000.0 (math/tan 1)))
=
DUST Result
-1557407724655
0
1557407724655

11.1.25. math/asin ↑

Function

(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>.

DUST
[(math/round (* 1000000000000.0 (math/asin -1))) (math/round (* 1000000000000.0 (math/asin 0)))
 (math/round (* 1000000000000.0 (math/asin 1)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/asin -1)))
(math/round (* 1000000000000.0 (math/asin 0)))
(math/round (* 1000000000000.0 (math/asin 1)))
=
DUST Result
-1570796326795
0
1570796326795

11.1.26. math/acos ↑

Function

(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>.

DUST
[(math/round (* 1000000000000.0 (math/acos -1))) (math/round (* 1000000000000.0 (math/acos 0)))
 (math/round (* 1000000000000.0 (math/acos 1)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/acos -1)))
(math/round (* 1000000000000.0 (math/acos 0)))
(math/round (* 1000000000000.0 (math/acos 1)))
=
DUST Result
3141592653590
1570796326795
0

11.1.27. math/atan ↑

Function

(math/atan n) → float

Returns the arc tangent of n in radians, in the range from negative pi/2 to pi/2.

DUST
[(math/round (* 1000000000000.0 (math/atan -1))) (math/round (* 1000000000000.0 (math/atan 0)))
 (math/round (* 1000000000000.0 (math/atan 1)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/atan -1)))
(math/round (* 1000000000000.0 (math/atan 0)))
(math/round (* 1000000000000.0 (math/atan 1)))
=
DUST Result
-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.

DUST
[(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)))]
→
Canonical DUST
(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)))
=
DUST Result
0
785398163397
2356194490192
-2356194490192
-785398163397
DUST
[(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))]
→
Canonical DUST
(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))
=
DUST Result
3141592653590
-3141592653590
-1.0

11.1.29. math/sinh ↑

Function

(math/sinh n) → float

Returns the hyperbolic sine of n.

DUST
[(math/round (* 1000000000000.0 (math/sinh -1))) (math/round (* 1000000000000.0 (math/sinh 0)))
 (math/round (* 1000000000000.0 (math/sinh 1)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/sinh -1)))
(math/round (* 1000000000000.0 (math/sinh 0)))
(math/round (* 1000000000000.0 (math/sinh 1)))
=
DUST Result
-1175201193644
0
1175201193644

11.1.30. math/cosh ↑

Function

(math/cosh n) → float

Returns the hyperbolic cosine of n.

DUST
[(math/round (* 1000000000000.0 (math/cosh -1))) (math/round (* 1000000000000.0 (math/cosh 0)))
 (math/round (* 1000000000000.0 (math/cosh 1)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/cosh -1)))
(math/round (* 1000000000000.0 (math/cosh 0)))
(math/round (* 1000000000000.0 (math/cosh 1)))
=
DUST Result
1543080634815
1000000000000
1543080634815

11.1.31. math/tanh ↑

Function

(math/tanh n) → float

Returns the hyperbolic tangent of n.

DUST
[(math/round (* 1000000000000.0 (math/tanh -1))) (math/round (* 1000000000000.0 (math/tanh 0)))
 (math/round (* 1000000000000.0 (math/tanh 1)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/tanh -1)))
(math/round (* 1000000000000.0 (math/tanh 0)))
(math/round (* 1000000000000.0 (math/tanh 1)))
=
DUST Result
-761594155956
0
761594155956

11.1.32. math/asinh ↑

Function

(math/asinh n) → float

Returns the inverse hyperbolic sine of n.

DUST
[(math/round (* 1000000000000.0 (math/asinh -1))) (math/round (* 1000000000000.0 (math/asinh 0)))
 (math/round (* 1000000000000.0 (math/asinh 1)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/asinh -1)))
(math/round (* 1000000000000.0 (math/asinh 0)))
(math/round (* 1000000000000.0 (math/asinh 1)))
=
DUST Result
-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>.

DUST
[(math/round (* 1000000000000.0 (math/acosh 1))) (math/round (* 1000000000000.0 (math/acosh 2)))
 (math/round (* 1000000000000.0 (math/acosh 3)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/acosh 1)))
(math/round (* 1000000000000.0 (math/acosh 2)))
(math/round (* 1000000000000.0 (math/acosh 3)))
=
DUST Result
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>.

DUST
[(math/round (* 1000000000000.0 (math/atanh -0.5))) (math/round (* 1000000000000.0 (math/atanh 0)))
 (math/round (* 1000000000000.0 (math/atanh 0.5)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/atanh -0.5)))
(math/round (* 1000000000000.0 (math/atanh 0)))
(math/round (* 1000000000000.0 (math/atanh 0.5)))
=
DUST Result
-549306144334
0
549306144334

11.1.35. math/radians ↑

Function

(math/radians degrees) → float

Returns the angle degrees converted to radians.

DUST
[(math/round (* 1000000000000.0 (math/radians -90)))
 (math/round (* 1000000000000.0 (math/radians 0)))
 (math/round (* 1000000000000.0 (math/radians 180)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/radians -90)))
(math/round (* 1000000000000.0 (math/radians 0)))
(math/round (* 1000000000000.0 (math/radians 180)))
=
DUST Result
-1570796326795
0
3141592653590

11.1.36. math/degrees ↑

Function

(math/degrees radians) → float

Returns the angle radians converted to degrees.

DUST
[(math/round (* 1000000000000.0 (math/degrees -1)))
 (math/round (* 1000000000000.0 (math/degrees 0)))
 (math/round (* 1000000000000.0 (math/degrees 1)))]
→
Canonical DUST
(math/round (* 1000000000000.0 (math/degrees -1)))
(math/round (* 1000000000000.0 (math/degrees 0)))
(math/round (* 1000000000000.0 (math/degrees 1)))
=
DUST Result
-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.

DUST
[(math/signum -3) (math/signum 0) (math/signum 5.5) (math/signum (- 0.0))]
→
Canonical DUST
(math/signum -3)
(math/signum 0)
(math/signum 5.5)
(math/signum (- 0.0))
=
DUST Result
-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.

DUST
[(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)]
→
Canonical DUST
(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)
=
DUST Result
#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>.

DUST
[(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)]
→
Canonical DUST
(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)
=
DUST Result
1
-2
-2
1
9223372036854775807
-9223372036854775808
2
-3
DUST
(math/floor-div -9223372036854775808 -1)
=
DUST Result
#err <integer overflow>

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.

DUST
[(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)]
→
Canonical DUST
(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)
=
DUST Result
2
1
-1
-2
0
1
1
1

11.1.41. math/abs ↑

Function

(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.

DUST
(math/abs -3)
(math/abs -0.5)
(math/abs 0)
=
DUST Result
3
0.5
0
DUST
(math/abs -9223372036854775808)
=
DUST Result
#err <integer overflow>

11.1.42. math/sign ↑

Function

(math/sign number) → integer

Returns integer -1, 0, or 1 according to the argument's sign.

DUST
(math/sign -0.5)
(math/sign 0)
(math/sign 42)
=

11.1.43. math/lerp ↑

Function

(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.

DUST
(math/lerp 10.0 20.0 0.5)
(math/lerp 10.0 20.0 1.5)
(math/lerp 10 20 0)
=
DUST Result
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.

DUST
(math/inverse-lerp 10 20 15)
(math/inverse-lerp 10 20 25)
(math/inverse-lerp 10 20 10)
=
DUST Result
0.5
1.5
0.0
DUST
(math/inverse-lerp 1 1 2)
=
DUST Result
#err <division integer zero>

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.

DUST
(math/remap 0 10 100 200 5)
(math/remap 0 10 100 200 15)
(math/remap 10 0 100 200 5)
=
DUST Result
150.0
250.0
150.0
DUST
(math/remap 1 1 2 3 4)
=
DUST Result
#err <division integer zero>

11.1.46. math/zero? ↑

Function

(math/zero? n) → boolean

Returns true if n is zero, otherwise false.

DUST
(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)
=
DUST Result
true
true
false
false
false
false
false
false
DUST
{boolean (math/zero? false)
 extra (math/zero? 0 0)
 list (math/zero? [])
 missing (math/zero?)
 null-input (math/zero? null)
 string (math/zero? "0")}
→
Evaluation cases
#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}]

11.1.47. math/odd? ↑

Function

(math/odd? n) → boolean

Returns true if integer n is odd, otherwise false.

DUST
(math/odd? 1)
(math/odd? 2)
(math/odd? -3)
(math/odd? 0)
(math/odd? -2)
(math/odd? -9223372036854775808)
(math/odd? 9223372036854775807)
=
DUST Result
true
false
true
false
false
false
true
DUST
{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")}
→
Evaluation cases
#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.

DUST
(math/even? 0)
(math/even? 2)
(math/even? -3)
(math/even? 1)
(math/even? -2)
(math/even? -9223372036854775808)
(math/even? 9223372036854775807)
=
DUST Result
true
true
false
false
true
true
false
DUST
{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")}
→
Evaluation cases
#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.49. math/min ↑

Function

(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.

DUST
(math/min 3 1 2)
(math/min 1.5 0.5)
(math/min 9007199254740994 9007199254740993)
=
DUST Result
1
0.5
9007199254740993
DUST
(math/min 1 1.0)
=
DUST Result
#err <mismatch type>

11.1.50. math/max ↑

Function

(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.

DUST
(math/max 3 1 2)
(math/max 1.5 0.5)
(math/max -9007199254740994 -9007199254740993)
=
DUST Result
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.

DUST
(math/clamp 5 1 3)
(math/clamp -1.0 0.0 1.0)
(math/clamp 2 1 3)
=
DUST Result
3
0.0
2
DUST
(math/clamp 1 2 0)
=
DUST Result
#err <invalid value>

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.

DUST
(time/utc -9223372036854775808)
(time/utc 9223372036854775807)
(time/utc 30005-01-01T00:00:00Z)
=
DUST Result
#utc -27258-04-19T21:11:54.5224192Z
#utc 31197-09-14T02:48:05.4775807Z
#utc 30005-01-01T00:00:00Z
DUST
(time/duration -9223372036854775808)
(time/duration 9223372036854775807)
(time/units ns (time/duration 1))
=
DUST Result
#dur -P10675199DT2H48M5.4775808S
#dur P10675199DT2H48M5.4775807S
100
DUST
#utc 2024-02-29T12:34:56.123456700Z
#utc 2024-02-29T12:34:56.12345670Z
→
Canonical DUST
#utc 2024-02-29T12:34:56.1234567Z
#utc 2024-02-29T12:34:56.1234567Z
=
DUST Result
#utc 2024-02-29T12:34:56.1234567Z
#utc 2024-02-29T12:34:56.1234567Z
DUST
#dur PT0.000000100S
#dur PT0.00000010S
→
Canonical DUST
#dur PT0.0000001S
#dur PT0.0000001S
=
DUST Result
#dur PT0.0000001S
#dur PT0.0000001S
DUST
#utc 1970-01-01T00:00:00.000000001Z
→
Canonical DUST
#utc 1970-01-01T00:00:00.0000001Z
=
DUST Result
#utc 1970-01-01T00:00:00.0000001Z
DUST
#dur PT0.000000001S
→
Canonical DUST
#dur PT0.0000001S
=
DUST Result
#dur PT0.0000001S

12.2. Types and conversion ↑

12.2.1. time/utc ↑

Function

(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.

DUST
(time/utc 0)
(time/utc -1)
(time/utc 2024-02-29T12:34:56.1234567Z)
=
DUST Result
#utc 1970-01-01T00:00:00Z
#utc 1969-12-31T23:59:59.9999999Z
#utc 2024-02-29T12:34:56.1234567Z
DUST
(time/utc yesterday)
=
DUST Result
#err <invalid value>
DUST
(time/utc 0.0)
(time/utc 2440587.5)
(time/utc 2451545.0)
=
DUST Result
#utc -4713-11-24T12:00:00Z
#utc 1970-01-01T00:00:00Z
#utc 2000-01-01T12:00:00Z
DUST
(time/utc 40000-01-01T00:00:00Z)
=
DUST Result
#err <invalid value>
DUST
(time/utc 1970-01-01T00:00:00.000000001Z)
=
DUST Result
#utc 1970-01-01T00:00:00.0000001Z

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.

DUST
(time/duration 0)
(time/duration -1)
(time/duration PT1H30M)
=
DUST Result
#dur PT0S
#dur -PT0.0000001S
#dur PT1H30M
DUST
(time/duration forever)
=
DUST Result
#err <invalid value>
DUST
(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)
=
DUST Result
#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
DUST
(time/duration 1 ns)
=
DUST Result
#dur PT0.0000001S
DUST
(time/duration 1.5 ticks)
=
DUST Result
#dur PT0.0000002S
DUST
(time/duration 1.5)
(time/duration -1.5)
(time/duration 1.4)
=
DUST Result
#dur PT0.0000002S
#dur -PT0.0000002S
#dur PT0.0000001S
DUST
(time/duration 1 year)
=
DUST Result
#err <invalid value>

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:

DUST
(time/units ticks (time/utc 1))
(time/units ticks (time/duration 1 ticks))
(time/units ticks (time/duration -9223372036854775808))
=
DUST Result
1
1
-9223372036854775808

Nanosecond results are also integers:

DUST
(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)
=
DUST Result
0
1500000000
-100

Larger units retain fractional duration values:

DUST
(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))
=
DUST Result
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:

DUST
(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))
=
DUST Result
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.

DUST
(time/units ns (time/duration 9223372036854775807))
=
DUST Result
#err <integer overflow>

See the time/units conformance cases.

12.2.4. time/utc? ↑

Function

(time/utc? value) → boolean

Returns whether the value has the UTC kind. Durations and integers return false.

DUST
(time/utc? #utc 1970-01-01T00:00Z)
(time/utc? #dur PT0S)
(time/utc? 0)
→
Canonical DUST
(time/utc? #utc 1970-01-01T00:00:00Z)
(time/utc? #dur PT0S)
(time/utc? 0)
=
DUST Result
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.

DUST
(time/duration? #dur PT0S)
(time/duration? #utc 1970-01-01T00:00Z)
(time/duration? 0)
→
Canonical DUST
(time/duration? #dur PT0S)
(time/duration? #utc 1970-01-01T00:00:00Z)
(time/duration? 0)
=
DUST Result
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.

DUST
(time/format #utc 1970-01-01T00:00:00Z)
(time/format #utc 1969-12-31T23:59:59.9999999Z)
(time/format #dur PT1.5S)
=
DUST Result
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.

DUST
(+ #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)
=
DUST Result
#utc 1970-01-01T00:00:01Z
#utc 1970-01-01T00:00:01Z
#dur PT3S
#utc 1970-01-01T00:00:00Z
#dur PT1S
DUST
(- #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)
=
DUST Result
#dur PT1S
#utc 1969-12-31T23:59:59Z
#dur PT2S
#dur -PT1S
DUST
(* #dur PT1.5S 2)
(* 2 #dur PT1.5S)
(* #dur PT1S)
(* 2 #dur PT3S 2)
=
DUST Result
#dur PT3S
#dur PT3S
#dur PT1S
#dur PT12S
DUST
(/ #dur PT3S 2)
(/ #dur PT0.0000001S -2)
(/ #dur -PT0.0000003S 2)
(/ #dur PT3S #dur PT2S)
=
DUST Result
#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.

DUST
(math/abs #dur -PT1S)
(math/abs #dur PT0S)
(math/abs #dur PT1.5S)
=
DUST Result
#dur PT1S
#dur PT0S
#dur PT1.5S
DUST
(math/min #dur PT2S #dur PT1S)
(math/min #utc 1970-01-02T00:00Z #utc 1970-01-01T00:00Z)
(math/min #dur -PT1S #dur PT0S)
→
Canonical DUST
(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)
=
DUST Result
#dur PT1S
#utc 1970-01-01T00:00:00Z
#dur -PT1S
DUST
(math/max #dur PT2S #dur PT1S)
(math/max #utc 1970-01-02T00:00Z #utc 1970-01-01T00:00Z)
(math/max #dur -PT1S #dur PT0S)
→
Canonical DUST
(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)
=
DUST Result
#dur PT2S
#utc 1970-01-02T00:00:00Z
#dur PT0S
DUST
(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)
→
Canonical DUST
(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)
=
DUST Result
#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.

DUST
(+ #utc 1970-01-01T00:00Z #utc 1970-01-01T00:00Z)
→
Canonical DUST
(+ #utc 1970-01-01T00:00:00Z #utc 1970-01-01T00:00:00Z)
=
DUST Result
#err <mismatch type>
DUST
(+ #dur PT1S 1)
=
DUST Result
#err <mismatch type>
DUST
(- #dur PT1S #utc 1970-01-01T00:00Z)
→
Canonical DUST
(- #dur PT1S #utc 1970-01-01T00:00:00Z)
=
DUST Result
#err <mismatch type>
DUST
(- (time/utc 1))
=
DUST Result
#err <mismatch type>
DUST
(* #dur PT1S 2.0)
=
DUST Result
#err <mismatch type>
DUST
(/ #dur PT1S)
=
DUST Result
#err <mismatch type>
DUST
(/ 2 #dur PT1S)
=
DUST Result
#err <mismatch type>
DUST
(/ #dur PT1S 0)
=
DUST Result
#err <division integer zero>
DUST
(/ #dur PT1S #dur PT0S)
=
DUST Result
#err <division integer zero>
DUST
(math/min #dur PT1S #utc 1970-01-01T00:00Z)
→
Canonical DUST
(math/min #dur PT1S #utc 1970-01-01T00:00:00Z)
=
DUST Result
#err <mismatch type>
DUST
(math/clamp #dur PT1S #dur PT2S #dur PT0S)
=
DUST Result
#err <invalid value>
DUST
(+ #dur PT922337203685.4775807S #dur PT0.0000001S)
→
Canonical DUST
(+ #dur P10675199DT2H48M5.4775807S #dur PT0.0000001S)
=
DUST Result
#err <integer overflow>
DUST
(/ #dur -PT922337203685.4775808S -1)
→
Canonical DUST
(/ #dur -P10675199DT2H48M5.4775808S -1)
=
DUST Result
#err <integer overflow>
DUST
(math/abs #dur -PT922337203685.4775808S)
→
Canonical DUST
(math/abs #dur -P10675199DT2H48M5.4775808S)
=
DUST Result
#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.

DUST
(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)
=
DUST Result
{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}
DUST
(time/parts #dur PT1S)
=
DUST Result
#err <mismatch type>

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.

DUST
(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)
=
DUST Result
#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
DUST
(time/start-of #utc 1970-01-01T00:00Z fortnight)
→
Canonical DUST
(time/start-of #utc 1970-01-01T00:00:00Z fortnight)
=
DUST Result
#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.

DUST
(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})
→
Canonical DUST
(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})
=
DUST Result
#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
DUST
(time/shift #utc 1970-01-01T00:00Z {hours 1})
→
Canonical DUST
(time/shift #utc 1970-01-01T00:00:00Z {hours 1})
=
DUST Result
#err <invalid value>
DUST
(time/shift #utc 1970-01-01T00:00Z {months 1.5})
→
Canonical DUST
(time/shift #utc 1970-01-01T00:00:00Z {months 1.5})
=
DUST Result
#err <mismatch type>
DUST
(time/shift (time/utc 9223372036854775807) {days 1})
=
DUST Result
#err <integer overflow>

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.

DUST
(time/truncate #dur PT1.9S #dur PT1S)
(time/truncate #dur -PT1.9S #dur PT1S)
(time/truncate #dur PT0.5S #dur PT1S)
=
DUST Result
#dur PT1S
#dur -PT1S
#dur PT0S
DUST
(time/truncate #dur PT1S #dur PT0S)
=
DUST Result
#err <invalid value>

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.

DUST
(time/round #dur PT1.5S #dur PT1S)
(time/round #dur -PT1.5S #dur PT1S)
(time/round #dur PT1.4S #dur PT1S)
=
DUST Result
#dur PT2S
#dur -PT2S
#dur PT1S
DUST
(time/round #dur PT1S #dur -PT1S)
=
DUST Result
#err <invalid value>
DUST
(time/round (time/duration 9223372036854775807) (time/duration 4))
=
DUST Result
#err <integer overflow>

12.6. time/now ↑

Function

(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)})
DUST
(fn? :time/now)
(int? (time/now))
(string? (time/now))
=
DUST Result
true
false
false
DUST
(time/now 1)
=
DUST Result
#err <arity mismatch>

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 n divided by d and each additional divisor in more, or the reciprocal of n when 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 true if all supplied values are equal, otherwise false.

    (= 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 #err value is falsey, so and returns it. If every form is truthy, returns the last value. With no forms, returns true.

    (and forms*) → value

  • apply — Calls a function value with the elements of a list as its arguments.

    (apply function arguments) → value

  • boolean — Returns false if value is null or false, otherwise true.

    (boolean value) → boolean

  • boolean? — Returns true if b is either true or false, otherwise false.

    (boolean? b) → boolean

  • collections/copy — 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.

    (collections/copy value) → value
    (collections/copy value shallow) → value

  • collections/update — 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.

    (collections/update root path f args*) → collection

  • compare — Returns -1, 0, or 1 when first sorts before, ties with, or sorts after second, using sort ordering.

    (compare first second) → integer

  • comparison/identical? — 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/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 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? v) → boolean

  • contains? — Returns true if path selects a value in root, including null or false, otherwise false.

    (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 1 and float 1.0 are separate values. Functions compare by identity. Positive and negative floating zero compare equal.

    (distinct list) → list

  • do — Evaluates the expr forms in order and returns the last result.

    (do expr*) → value

  • empty? — Returns whether count is zero. Lists, pointers, maps, strings, and null are accepted.

    (empty? collection) → boolean

  • error? — Returns true if value is an error and false otherwise. 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 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.

    (export name*) → list

  • false? — Returns true if value is the boolean false, otherwise false.

    (false? value) → boolean

  • first — Returns the first element or pointer segment, or null when 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 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.

    (float value) → float

  • float? — Returns true if n is a floating-point number, otherwise false.

    (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, returns f.

    (fn/comp) → function
    (fn/comp f) → function
    (fn/comp f g functions*) → function

  • fn/complement — Returns a function that calls f with 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, 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 f x) → function
    (fn/fnull f x y) → function
    (fn/fnull f x y z) → function

  • fn/identity — Returns value unchanged.

    (fn/identity value) → value

  • fn/juxt — Returns a function that applies f and functions to the same arguments and returns their results in order as a list.

    (fn/juxt f functions*) → function

  • fn/partial — Returns a function that calls f with args followed by its call-time arguments. With no fixed arguments, returns f.

    (fn/partial f args*) → function

  • fn? — Returns true if f is a function, otherwise false.

    (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 args into the conversion specifications in template.

    (format template args*) → string

  • from-pairs — Returns a fresh map from [key value] pairs in list. Later duplicate keys win.

    (from-pairs list) → map

  • get — 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.

    (get root path) → value
    (get root path default) → value

  • hash/digest — Returns a #hash of value under algorithm, 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 true if n is an integer, otherwise false. 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 ~0 and ~1 escapes. Invalid text gives #err <invalid value>; a non-string gives #err <mismatch type>.

    (jsonptr text) → pointer

  • jsonptr? — Returns true for a pointer and false for 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 true if xs is a list, otherwise false.

    (list? xs) → boolean

  • lists/any? — Returns true at the first truthy predicate result without visiting later elements. Returns false for an empty list.

    (lists/any? predicate list) → boolean

  • lists/append — 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.

    (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 n elements of list.

    (lists/drop n list) → list

  • lists/drop-while — Returns list from the first element for which predicate is falsey, skipping the preceding elements.

    (lists/drop-while predicate list) → list

  • lists/every? — Returns false at the first falsey predicate result without visiting later elements. Returns true for an empty list.

    (lists/every? predicate list) → boolean

  • lists/filter — Calls predicate on 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 list for which predicate is truthy, or default if none matches. The default is null.

    (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, 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 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 1 is never coerced to string "1".

    (lists/frequencies strings) → map

  • lists/group-by — 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 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 separator between adjacent elements of list.

    (lists/interpose separator list) → list

  • lists/keep — Returns the results of applying f to each element of list, excluding null but preserving false and input order.

    (lists/keep f list) → list

  • lists/last — Returns the last element, or null for an empty list.

    (lists/last list) → value

  • lists/mapcat — 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 f list lists*) → list

  • lists/nth — 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.

    (lists/nth list index default?) → value

  • lists/partition — Returns groups of up to n elements from list, starting step elements apart and including short trailing groups. step defaults to n.

    (lists/partition n list) → list
    (lists/partition n step list) → list

  • lists/partition-by — Returns consecutive groups from list, starting a new group whenever f returns a different value.

    (lists/partition-by f list) → list

  • lists/range — 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 end) → list
    (lists/range start end step?) → list

  • lists/repeat — 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 count value) → list

  • lists/reverse — Reverses list in place and returns the same list, without copying the elements. list must be a list, not null or a pointer.

    (lists/reverse list) → list

  • lists/second — Returns the second element of list, or null if list has fewer than two elements.

    (lists/second list) → value

  • lists/slice — 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 list start end?) → list

  • lists/some — Returns the first truthy result of applying f to elements of list, without visiting later elements, or null if none matches.

    (lists/some f list) → value

  • lists/sort-by — 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 f list) → list
    (lists/sort-by f comparator list) → list

  • lists/take — Returns a fresh list of the first n elements of list, or all elements if fewer are available.

    (lists/take n list) → list

  • lists/take-while — Returns the leading elements of list for which predicate is 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 true if v is a local reference, such as the quoted form `.name, otherwise false.

    (local? v) → boolean

  • map — Returns a list of the results of applying f to corresponding elements of xs and any additional lists.

    (map f xs) → list
    (map f xs lists*) → list

  • map? — Returns true if m is a map, otherwise false.

    (map? m) → boolean

  • maps/select-keys — Returns a fresh map containing the literal top-level keys in keys that are present in map.

    (maps/select-keys map keys) → map

  • maps/zipmap — Returns a fresh map pairing string keys with values, 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 n in 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 n in 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 n in 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 as n.

    (math/cbrt n) → float

  • math/ceil — Returns the least integral-valued float greater than or equal to n after conversion to a float.

    (math/ceil n) → float

  • math/clamp — 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.

    (math/clamp value low high) → value

  • math/copy-sign — Returns the absolute value of magnitude with the sign of sign, including the sign of floating-point zero.

    (math/copy-sign magnitude sign) → float

  • math/cos — Returns the cosine of angle in radians.

    (math/cos angle) → float

  • math/cosh — Returns the hyperbolic cosine of n.

    (math/cosh n) → float

  • math/degrees — Returns the angle radians converted to degrees.

    (math/degrees radians) → float

  • math/even? — Returns true if integer n is even, otherwise false.

    (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 n minus one, computed without the loss of precision from subtracting one from math/exp's rounded result near zero.

    (math/expm1 n) → float

  • math/floor — Returns the greatest integral-valued float less than or equal to n after conversion to a float.

    (math/floor n) → float

  • math/floor-div — Returns the integer quotient of n divided by d, rounded toward negative infinity.

    (math/floor-div n d) → integer

  • math/floor-mod — Returns the integer remainder of n divided by d using a quotient rounded toward negative infinity.

    (math/floor-mod n d) → integer

  • math/hypot — Returns the square root of x*x + y*y without intermediate overflow or underflow.

    (math/hypot x y) → float

  • math/inverse-lerp — Returns the fraction locating value between start and end, without clamping. Equal endpoints are an error.

    (math/inverse-lerp start end value) → float

  • math/lerp — 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 start end fraction) → float

  • math/log — 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.

    (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 rounding 1 + n near 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 true if integer n is odd, otherwise false.

    (math/odd? n) → boolean

  • math/pow — Returns base raised to exponent.

    (math/pow base exponent) → float

  • math/radians — Returns the angle degrees converted to radians.

    (math/radians degrees) → float

  • math/remap — Maps value from 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 n after 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, or 1 according to the argument's sign.

    (math/sign number) → integer

  • math/signum — Returns -1.0 for negative n, 1.0 for positive n, or zero with n's sign for zero.

    (math/signum n) → float

  • math/sin — Returns the sine of angle in 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 angle in radians.

    (math/tan angle) → float

  • math/tanh — Returns the hyperbolic tangent of n.

    (math/tanh n) → float

  • math/trunc — Returns n converted to a float with its fractional part discarded, rounding toward zero.

    (math/trunc n) → float

  • math/zero? — Returns true if n is zero, otherwise false.

    (math/zero? n) → boolean

  • not — Returns true if value is null or false, otherwise false.

    (not value) → boolean

  • null? — Returns true if x is null, otherwise false.

    (null? x) → boolean

  • number? — Returns true if n is an integer or a float, otherwise false. 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 by keys.

    (pairs map) → list

  • patch — 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.

    (patch map changes*) → map

  • put — 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.

    (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 or null produces #err <invalid value>.

    (random/choose generator list) → value

  • random/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.

    (random/float generator lo hi) → float

  • random/int — 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.

    (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 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 [].

    (random/shuffle generator list) → list

  • reduce — 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 function initial list) → value

  • regex/find — Returns the next match from matcher, or the first match of re in string s. Returns null if 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 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.

    (regex/find-all re s [opts]) → list

  • regex/groups — 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.

    (regex/groups matcher [opts]) → string, list, or map

  • regex/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.

    (regex/matcher re s) → matcher

  • regex/matches — Returns the match if re matches the entire string s, otherwise null. Results use the capture format of regex/groups.

    (regex/matches re s [opts]) → string, list, map, or null

  • regex/pattern — Returns a compiled pattern for string s, or s unchanged if it is already a pattern. Invalid patterns produce an error.

    (regex/pattern s) → pattern

  • regex/quote-replacement — Returns string replacement escaped for literal use with regex/replace or regex/replace-first.

    (regex/quote-replacement replacement) → string

  • regex/replace — Returns string s with all non-overlapping matches of re replaced by replacement.

    (regex/replace s re replacement [opts]) → string

  • regex/replace-first — Returns string s with the first match of re replaced by replacement. Uses the replacement rules of regex/replace.

    (regex/replace-first s re replacement [opts]) → string

  • regex/split — 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.

    (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 -1 has remainder zero.

    (rem dividend divisor) → number

  • remove — 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.

    (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 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.

    (sort list) → list
    (sort comparator list) → list

  • str — 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.

    (str values*) → string

  • string? — Returns true if s is a string, otherwise false.

    (string? s) → boolean

  • strings/blank? — Returns true if s is null, empty, or contains only whitespace, otherwise false.

    (strings/blank? s) → boolean

  • strings/case — 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.

    (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 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 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 value in s at or after start, or null if 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 separator between 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 value in s at or before start, or null if not found.

    (strings/last-index-of s value) → integer or null
    (strings/last-index-of s value start) → integer or null

  • strings/normalize — Returns s normalized to the Unicode normalization form, defaulting to NFC.

    (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 inserts replacement before, between, and after the input's Unicode code points: (strings/replace ab "" x) returns xaxbx.

    (strings/replace string search replacement) → string

  • strings/replace-first — Returns s with the first occurrence of match replaced by replacement.

    (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 s separated 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 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 string start) → string
    (strings/substring string start end) → string

  • strings/trim — Returns s with whitespace or text removed from the ends selected by opts.

    (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 true if value is a string template, otherwise false.

    (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/humanize function.

    (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, 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 timestamp) → map

  • time/round — 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 duration quantum) → duration

  • time/shift — 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.

    (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, 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 timestamp unit) → UTC

  • time/truncate — 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 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: day 0.0 is -4713-11-24T12:00:00Z, 2440587.5 is the Unix epoch, and 2451545.0 is 2000-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 true if value is the boolean true, otherwise false.

    (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...)

Footnotes:

1

For Lispers, DUST does not execute code during reads. There are no reader macros. Hence parser, not reader.

2

Because DUST forms have a JSON representation, keys are always (always!) strings.