Skip to content
ry

Standard library

ry ships a small standard library modeled on Roc’s builtins: Result, List, Str, Num, Bool, Dict and Set. Every function is fully typed, never meets NA, and reports failure as a tag instead of an error or a warning, so the pieces compose with match and the checker follows every step:

parse_ids <- function(text: character) -> Result<list[integer], :InvalidNumStr> {
parts <- List.map(Str.split_on(text, ","), Str.trim)
List.map_try(parts, Str.to_integer)
}
counts: Dict<character, integer> <- List.walk(list("a", "b", "a"), Dict.empty(), function(seen, word) {
Dict.update(seen, word, function(found) :Ok(Result.with_default(found, 0L) + 1L))
})
List.sum(parse_ids("3, 1, 2"))
type-mismatch
x expected `list[T]`, found `:Ok(list[integer]) | :Err(:InvalidNumStr)`

parse_ids("3, 1, 2") is :Ok(list(3L, 1L, 2L)), parse_ids("3, x") is :Err(:InvalidNumStr), and counts is Dict.from_list(list(("a", 2L), ("b", 1L))). The last line is the mistake the checker catches: the result has to be unwrapped, with match or Result.with_default, before its list can be summed.

The library runs only on ry’s runtime, so run such a script with ry run, not ry R. GNU R does not have these functions, and under ry R a call to one stops with an error that says so:

Error: `List.map()` is part of ry's standard library, which only ry's runtime provides, and this session runs on GNU R; run the code with `ry run` or `ry load` instead

Plain Rscript knows nothing of ry, so there the call fails with “could not find function”.

These rules hold in every module:

  • Names are plain R names. List.map is one symbol, called like any R function; there is no method syntax. The names live in ry’s ry package, attached at startup as package:ry, so a definition of your own, or one from a package you attach, comes first, and ry::List.map still reaches the library’s function.
  • A collection is an R list. List.map takes any list and returns a new one, and Str.split_on returns a list of strings. List.from_vector turns an atomic vector into a list, and List.to_vector turns a list of scalars back.
  • Positions are 1-based integers, as in xs[[1L]]. A whole double such as 2 is accepted too.
  • A string is a length-one character, and a number a length-one integer or double. NA and NaN are refused with an error, because no function has an NA case to fall into. The one exception is Num.is_nan, which accepts NaN because telling it apart is its job.
  • Misuse is an error, not a tag. An argument of the wrong type, an NA, or an integer List.sum or List.product that overflows stops with an R error that names the problem. So does an unchecked Num operation whose result does not exist, such as Num.div(1, 0), where Roc would crash; its _checked variant returns the failure as a tag instead.
  • No function returns NaN. A result that would be NaN, as Num.sin(Inf), Num.pow(-1, 0.5) and List.sum(list(Inf, -Inf)) would be, is an error, and a _checked function returns :Err(:Undefined) for it instead.
  • Failure is a tag. A function that can fail returns :Ok(value) or :Err(tag), typed Result<T, E>, and the tag names the reason: :OutOfBounds, :NotFound, :ListWasEmpty, :KeyNotFound, :InvalidNumStr, :BadUtf8, :BadUtf16, :BadUtf32, :Overflow, :DivByZero, :Undefined, :SqrtOfNegative or :LogNeedsPositive.
  • Equality is identical(), wherever a function compares values (List.contains, Dict.keep_shared).
  • Orderings are tags. Num.compare returns :LT, :EQ or :GT, and List.sort_with takes a function that does the same.
  • Every value is immutable. A function that “changes” a list, dictionary or set returns a new one.

A function that gets the wrong kind of value stops with an error that names the parameter:

Error in List.len(c(1, 2)) :
`list` must be a list, not a double vector of length 2

R has no dictionary or set with value semantics, so Dict and Set are values of their own, like ry’s records and tags. typeof() is "dict" or "set", and the class is ry_dict or ry_set. Both keep insertion order, print as the call that rebuilds them, and compare with identical() regardless of order:

d <- Dict.from_list(list(("b", 2), ("a", 1)))
d
#> Dict.from_list(list(("b", 2), ("a", 1)))
identical(d, Dict.from_list(list(("a", 1), ("b", 2))))
#> [1] TRUE

A key or element is a logical, integer, double or character scalar that is not NA or NaN. Every key of one collection has the same type, which the checker enforces. At run time integers and doubles mix, as in R’s own ==: 1L and 1 are the same key, and the keys become doubles. -0 and 0 are the same key too. identical() still tells an integer-keyed collection from a double-keyed one, as it tells 1L from 1.

length(), identical(), all.equal(), format(), deparse() and as.list() understand them. Any other R function refuses one, before it runs and in the checker, so convert with as.list(), which gives a list of (key, value) tuples or of elements:

Error in sum(d) :
`d` is a ry dict, not an R object; convert it with `as.list()`

A function that returns an updated collection, such as Dict.insert, Set.insert, List.append or List.set, changes its first argument in place when nothing else can see that value, and copies it otherwise. The in-place path applies to a reassignment of the form x <- Dict.insert(x, key, value), where the first argument is the variable being assigned and no other variable, list or closure holds the same value. A loop that grows a collection one entry at a time then costs amortized constant time per entry instead of a full copy:

seen <- Dict.empty()
for (i in 1:100000) seen <- Dict.insert(seen, i, i * 2L)
Dict.len(seen)
#> 100000L

When another binding holds the value, as in old <- seen; seen <- Dict.insert(seen, 0L, 0L), the function copies first, so old never changes. The semantics are the same either way; only the cost differs. List.reserve, Dict.reserve and Set.reserve set aside room ahead of a known number of insertions.

A collection inside a list updates in place too, when both sides name it through the same $ or [[ path and nothing else holds the lists on that path:

state <- list(seen = Dict.empty(), count = 0L)
for (i in 1:100000) state$seen <- Dict.insert(state$seen, i, TRUE)

A dictionary, set or list also updates in place through a helper function, when the call has the form x <- helper(x, ...) and the helper either reassigns its own parameter or returns the update of its first parameter directly, as in function(seen, key) Dict.insert(seen, key, TRUE):

remember <- function(seen, key) {
seen <- Dict.insert(seen, key, TRUE)
seen
}
seen <- Dict.empty()
for (i in 1:100000) seen <- remember(seen, i)

The value moves into the helper’s parameter for the duration of the call, so the parameter holds the only reference and its Dict.insert updates in place. The caller’s variable cannot change visibly while the helper runs, because the move happens only when nothing can read that variable before the assignment replaces it. The helper’s body and defaults, and the other arguments, must call nothing but closures and simple builtins (arithmetic, comparisons, indexing, paste, length, the standard library and similar), and must not name the caller’s variable unless it is the helper’s own parameter. The helper’s arguments must be positional, with no .... If code the check did not cover runs during the call, such as a closure the helper calls, the move ends and the caller’s variable gets its value back first. If the helper fails, the variable keeps the value it had before the call, even when the helper had already updated the collection.

A helper may define and call its own closures, as in inc <- function(x) x + 1, as long as each closure’s body passes the same check and none of them outlives the call.

Other helpers copy, as in GNU R, and so does a call whose result goes to another variable, as in y <- helper(x, k). A helper that reads a variable through an environment, as in env$x, ends the move at that point. For those, loop inside the helper, or keep the update in the loop that owns the variable:

remember_all <- function(seen, keys) {
for (key in keys) seen <- Dict.insert(seen, key, TRUE)
seen
}

Here only the first insertion copies, so the whole call is linear.

A collection holds keys of one type, so a key of another type is an error at run time rather than a missing key. On a dictionary with character keys, Dict.get(d, "z") is :Err(:KeyNotFound), but Dict.get(d, 1L) stops:

Error in Dict.get(d, 1L) :
the dict's keys are character, and this one is integer; a collection holds one type

Integer and double keys are the one exception: they compare as numbers, so 1L finds the key 1. With typing = true, the checker reports a mismatched key before the code runs:

type-mismatch
x expected `character`, found `integer`
--[a.R:2:20]
1 | d <- Dict.single("a", 1)
2 | got <- Dict.get(d, 1L)
| ^^

Hover and completion show each function’s signature and a one-line description.

Each function below shows its signature, what it does, and an example. The result of each example is shown as dput() writes it, which for a list is more compact than the console’s printing.

A result is :Ok(value) or :Err(error), typed Result<T, E>. The alias is built in, so every annotation can use it.

<T, E> fn(result: Result<T, E>) -> logical

Whether the result is :Ok.

Result.is_ok(:Ok(1L))
#> TRUE
<T, E> fn(result: Result<T, E>) -> logical

Whether the result is :Err.

Result.is_err(:Ok(1L))
#> FALSE
<T, E> fn(result: Result<T, E>, default: T) -> T

The :Ok value, or default for an :Err.

Result.with_default(:Err(:Missing), 0L)
#> 0L
<A, B, E> fn(result: Result<A, E>, f: fn(A) -> B) -> Result<B, E>

Applies f to an :Ok value and leaves an :Err alone.

Result.map(:Ok(2L), function(x) x * 10L)
#> :Ok(20L)
<T, A, B> fn(result: Result<T, A>, f: fn(A) -> B) -> Result<T, B>

Applies f to an :Err value and leaves an :Ok alone.

Result.map_err(:Err("disk full"), function(e) :IoError(e))
#> :Err(:IoError("disk full"))
<A, B, C, D> fn(result: Result<A, B>, on_ok: fn(A) -> C, on_err: fn(B) -> D) -> Result<C, D>

Applies on_ok to an :Ok value or on_err to an :Err value.

Result.map_both(:Err(404L), function(x) x, function(code) Str.concat("HTTP ", Num.to_str(code)))
#> :Err("HTTP 404")
<A, B, C, E, F> fn(first: Result<A, E>, second: Result<B, F>, f: fn(A, B) -> C) -> Result<C, E | F>

Combines two :Ok values with f, or returns the first :Err.

Result.map2(Str.to_integer("6"), Str.to_integer("7"), function(a, b) a * b)
#> :Ok(42L)
<A, B, E, F> fn(result: Result<A, E>, f: fn(A) -> Result<B, F>) -> Result<B, E | F>

Chains a step that can fail: f gets the :Ok value and returns the next result. The error type of the chain joins both, so a step can fail with a reason of its own.

Result.and_then(Str.to_integer("-3"), function(n) if (n > 0L) :Ok(n) else :Err(:NotPositive))
#> :Err(:NotPositive)
<T, A, B> fn(result: Result<T, A>, f: fn(A) -> Result<T, B>) -> Result<T, B>

Recovers from an :Err: f gets the error and returns the next result.

Result.on_err(Str.to_integer("x"), function(e) :Ok(0L))
#> :Ok(0L)

Functions over R lists, typed list[T], and over Arrays. Each function also takes an Array<T> as its collection (the list, first or lists argument) and then returns an Array where it would return a list, so List.map([1, 2, 3], \(x) x * 2) is the Array<double> [2, 4, 6]. An item argument does not count, so List.append(list([1]), [2]) is the list list([1], [2]), and List.single and List.repeat always return lists. A function whose result items come from a callback, such as List.map, requires them to be Array items (the item constraint), so List.map([1, 2], \(x) list(x)) is a type error. The signatures below show the list form.

<T> fn(vector: T[]) -> list[T]

The elements of an atomic vector as a list.

List.from_vector(c(3L, 1L, 2L))
#> list(3L, 1L, 2L)
<T> fn(list: list[T]) -> T[]

The elements of a list as an atomic vector; every element must be a scalar of one type.

List.to_vector(list(3L, 1L, 2L))
#> c(3L, 1L, 2L)
<T> fn(list: list[T]) -> integer

The number of elements.

List.len(list("a", "b"))
#> 2L
<T> fn(list: list[T]) -> logical

Whether the list has no elements.

List.is_empty(list())
#> TRUE
<T> fn(list: list[T], index: integer) -> Result<T, :OutOfBounds>

The element at a 1-based position, or :Err(:OutOfBounds).

List.get(list("a", "b"), 3L)
#> :Err(:OutOfBounds)
<T> fn(list: list[T], index: integer, value: T) -> list[T]

Replaces the element at a position; a position out of range leaves the list unchanged.

List.set(list("a", "b"), 2L, "z")
#> list("a", "z")
<T> fn(list: list[T], index: integer, f: fn(T) -> T) -> list[T]

Replaces the element at a position with f of it.

List.update(list(1L, 2L), 1L, function(x) x + 10L)
#> list(11L, 2L)
<T> fn(list: list[T], first: integer, second: integer) -> list[T]

Exchanges the elements at two positions; an out-of-range position leaves the list unchanged.

List.swap(list("a", "b", "c"), 1L, 3L)
#> list("c", "b", "a")
<T> fn(list: list[T], index: integer, value: T) -> Record{list: list[T], value: T}

Replaces the element at a position, returning the new list and the element it replaced.

List.replace(list("a", "b"), 2L, "z")
#> (list = list("a", "z"), value = "b")
<T> fn(list: list[T], value: T) -> list[T]

Adds an element at the end.

List.append(list(1L, 2L), 3L)
#> list(1L, 2L, 3L)
<T, E> fn(list: list[T], result: Result<T, E>) -> list[T]

Adds an :Ok value at the end; an :Err leaves the list unchanged.

List.append_if_ok(list(1L), Str.to_integer("2"))
#> list(1L, 2L)
<T> fn(list: list[T], value: T) -> list[T]

Adds an element at the start.

List.prepend(list(1L, 2L), 0L)
#> list(0L, 1L, 2L)
<T, E> fn(list: list[T], result: Result<T, E>) -> list[T]

Adds an :Ok value at the start; an :Err leaves the list unchanged.

List.prepend_if_ok(list(1L), Str.to_integer("x"))
#> list(1L)
<T> fn(first: list[T], second: list[T]) -> list[T]

The elements of first followed by those of second.

List.concat(list(1L), list(2L, 3L))
#> list(1L, 2L, 3L)
fn(list: list[integer], string: character) -> list[integer]

Appends the UTF-8 bytes of a string to a list of bytes.

List.concat_utf8(list(104L), "i")
#> list(104L, 105L)
<T> fn(value: T) -> list[T]

A list of one element.

List.single("only")
#> list("only")
<T> fn(value: T, count: integer) -> list[T]

A list of count copies of value.

List.repeat("x", 3L)
#> list("x", "x", "x")
fn(start: integer, end: integer) -> list[integer]

The integers from start up to and including end, or an empty list when end is smaller. Unlike R’s :, it never counts down.

Both List.range and List.repeat stop with an error when the list would hold more than 2,147,483,647 elements, the longest vector an R integer index reaches.

List.range(2L, 5L)
#> list(2L, 3L, 4L, 5L)
List.range(3L, 1L)
#> list()
<T> fn(capacity: integer) -> list[T]

An empty list with room for capacity elements, so appending that many never copies.

List.len(List.with_capacity(1000L))
#> 0L
<T> fn(list: list[T], spare: integer) -> list[T]

The list with room for spare more elements.

List.reserve(list(1L, 2L), 100L)
#> list(1L, 2L)
<T> fn(list: list[T]) -> list[T]

The list with no room beyond its elements, which gives back what List.reserve or List.with_capacity set aside.

List.release_excess_capacity(List.reserve(list(1L, 2L), 100L))
#> list(1L, 2L)
<T> fn(list: list[T]) -> list[T]

The elements in reverse order.

List.reverse(list(1L, 2L, 3L))
#> list(3L, 2L, 1L)
<T> fn(lists: list[list[T]]) -> list[T]

Concatenates a list of lists.

List.join(list(list(1L), list(), list(2L, 3L)))
#> list(1L, 2L, 3L)
<A, B> fn(list: list[A], f: fn(A) -> B) -> list[B]

Applies f to every element.

List.map(list(1L, 2L, 3L), function(x) x * 2L)
#> list(2L, 4L, 6L)
<A, B> fn(list: list[A], f: fn(A, integer) -> B) -> list[B]

Applies f to every element and its 1-based position.

List.map_with_index(list("a", "b"), function(x, i) Str.concat(x, Num.to_str(i)))
#> list("a1", "b2")
<A, B, C> fn(first: list[A], second: list[B], f: fn(A, B) -> C) -> list[C]

Applies f to the elements of two lists pairwise, stopping at the shorter one.

List.map2(list(1L, 2L, 3L), list(10L, 20L), function(a, b) a + b)
#> list(11L, 22L)
<A, B, C, D> fn(first: list[A], second: list[B], third: list[C], f: fn(A, B, C) -> D) -> list[D]

Applies f to the elements at each position of three lists, up to the shortest one.

List.map3(list(1L, 2L), list(10L, 20L), list(100L, 200L), function(a, b, c) a + b + c)
#> list(111L, 222L)
<A, B, C, D, E> fn(first: list[A], second: list[B], third: list[C], fourth: list[D], f: fn(A, B, C, D) -> E) -> list[E]

Applies f to the elements at each position of four lists, up to the shortest one.

List.map4(list(1L), list(2L), list(3L), list(4L), function(a, b, c, d) a * b * c * d)
#> list(24L)
<A, B> fn(list: list[A], f: fn(A) -> list[B]) -> list[B]

Applies f, which returns a list, to every element and concatenates the results.

List.join_map(list(1L, 2L), function(x) list(x, x))
#> list(1L, 1L, 2L, 2L)
<T> fn(list: list[T], predicate: fn(T) -> logical) -> list[T]

The elements for which predicate is TRUE.

List.keep_if(list(1L, 5L, 2L), function(x) x > 1L)
#> list(5L, 2L)
<T, E> fn(list: list[T], predicate: fn(T) -> Result<logical, E>) -> Result<list[T], E>

The elements for which predicate returns :Ok(TRUE), or the first :Err it returns.

List.keep_if_try(list("1", "20", "3"), function(s) Result.map(Str.to_integer(s), function(n) n > 2L))
#> :Ok(list("20", "3"))
List.keep_if_try(list("1", "x"), function(s) Result.map(Str.to_integer(s), function(n) n > 2L))
#> :Err(:InvalidNumStr)
<T> fn(list: list[T], predicate: fn(T) -> logical) -> list[T]

The elements for which predicate is FALSE.

List.drop_if(list(1L, 5L, 2L), function(x) x > 1L)
#> list(1L)
<A, B, E> fn(list: list[A], f: fn(A) -> Result<B, E>) -> list[B]

Applies f to every element and keeps the :Ok values.

List.keep_oks(list("1", "x", "3"), Str.to_integer)
#> list(1L, 3L)
<A, B, E> fn(list: list[A], f: fn(A) -> Result<B, E>) -> list[E]

Applies f to every element and keeps the :Err values.

List.keep_errs(list("1", "x"), Str.to_integer)
#> list(:InvalidNumStr)
<A, B, E> fn(list: list[A], f: fn(A) -> Result<B, E>) -> Result<list[B], E>

Applies f to every element and stops at the first :Err.

List.map_try(list("1", "2"), Str.to_integer)
#> :Ok(list(1L, 2L))
<T, S> fn(list: list[T], state: S, f: fn(S, T) -> S) -> S

Folds from the first element: f gets the state and an element and returns the next state.

List.walk(list(1L, 2L, 3L), 0L, function(total, x) total + x)
#> 6L
<T, S> fn(list: list[T], state: S, f: fn(S, T) -> S) -> S

Folds from the last element.

List.walk_backwards(list("a", "b", "c"), "", function(text, x) Str.concat(text, x))
#> "cba"
<T, S> fn(list: list[T], state: S, f: fn(S, T) -> :Continue(S) | :Break(S)) -> S

Folds until f returns :Break(state); :Continue(state) goes on.

List.walk_until(list(1L, 2L, 3L, 4L), 0L, function(total, x) if (total >= 3L) :Break(total) else :Continue(total + x))
#> 3L
<T, S> fn(list: list[T], state: S, f: fn(S, T) -> :Continue(S) | :Break(S)) -> S

Folds the list from the last element until f returns :Break(state).

List.walk_backwards_until(list(1L, 5L, 2L, 3L), 0L, function(total, x) if (x > 4L) :Break(total) else :Continue(total + x))
#> 5L
<T, S> fn(list: list[T], state: S, f: fn(S, T, integer) -> S) -> S

Folds the list, passing f each element’s 1-based position too.

List.walk_with_index(list(10L, 20L, 30L), 0L, function(total, x, i) total + x * i)
#> 140L
<T, S> fn(list: list[T], state: S, f: fn(S, T, integer) -> :Continue(S) | :Break(S)) -> S

Folds the list with positions until f returns :Break(state).

List.walk_with_index_until(list("a", "b", "c"), "", function(seen, x, i) if (i > 2L) :Break(seen) else :Continue(Str.concat(seen, x)))
#> "ab"
<T, S> fn(list: list[T], index: integer, state: S, f: fn(S, T) -> S) -> S

Folds the list from a 1-based position onwards.

List.walk_from(list(1L, 2L, 3L, 4L), 3L, 0L, function(total, x) total + x)
#> 7L
<T, S> fn(list: list[T], index: integer, state: S, f: fn(S, T) -> :Continue(S) | :Break(S)) -> S

Folds the list from a position until f returns :Break(state).

List.walk_from_until(list(1L, 2L, 3L, 4L), 2L, 0L, function(total, x) if (total >= 5L) :Break(total) else :Continue(total + x))
#> 5L
<T, S, E> fn(list: list[T], state: S, f: fn(S, T) -> Result<S, E>) -> Result<S, E>

Folds the list while f returns :Ok(state), stopping at the first :Err.

List.walk_try(list("1", "2", "x"), 0L, function(total, s) Result.map(Str.to_integer(s), function(n) total + n))
#> :Err(:InvalidNumStr)
<T> fn(list: list[T], f: fn(T) -> Any) -> NULL

Calls f on each element for its effect, and returns NULL invisibly.

List.for_each(list("a", "b"), function(x) cat(x, "\n"))
#> a
#> b
<T, E> fn(list: list[T], f: fn(T) -> Result<Any, E>) -> Result<NULL, E>

Calls f on each element, stopping at the first :Err.

List.for_each_try(list(1L, -2L, 3L), function(x) if (x < 0L) :Err(:Negative(x)) else :Ok(NULL))
#> :Err(:Negative(-2L))
<T> fn(list: list[T], predicate: fn(T) -> logical) -> logical

Whether predicate is TRUE for some element.

List.any(list(1L, 5L), function(x) x > 4L)
#> TRUE
<T> fn(list: list[T], predicate: fn(T) -> logical) -> logical

Whether predicate is TRUE for every element; TRUE for an empty list.

List.all(list(1L, 5L), function(x) x > 4L)
#> FALSE
<T> fn(list: list[T], value: T) -> logical

Whether some element is identical() to value.

List.contains(list("a", "b"), "b")
#> TRUE
<T> fn(list: list[T], predicate: fn(T) -> logical) -> integer

The number of elements for which predicate is TRUE.

List.count_if(list(1L, 5L, 7L), function(x) x > 4L)
#> 2L
<T> fn(list: list[T], predicate: fn(T) -> logical) -> Result<T, :NotFound>

The first element for which predicate is TRUE, or :Err(:NotFound).

List.find_first(list(1L, 5L, 7L), function(x) x > 4L)
#> :Ok(5L)
<T> fn(list: list[T], predicate: fn(T) -> logical) -> Result<T, :NotFound>

The last element for which predicate is TRUE, or :Err(:NotFound).

List.find_last(list(1L, 5L, 7L), function(x) x > 4L)
#> :Ok(7L)
<T> fn(list: list[T], predicate: fn(T) -> logical) -> Result<integer, :NotFound>

The position of the first match, or :Err(:NotFound).

List.find_first_index(list(1L, 5L, 7L), function(x) x > 4L)
#> :Ok(2L)
<T> fn(list: list[T], predicate: fn(T) -> logical) -> Result<integer, :NotFound>

The position of the last match, or :Err(:NotFound).

List.find_last_index(list(1L, 5L, 7L), function(x) x > 9L)
#> :Err(:NotFound)
<T> fn(list: list[T]) -> Result<T, :ListWasEmpty>

The first element, or :Err(:ListWasEmpty).

List.first(list("a", "b"))
#> :Ok("a")
<T> fn(list: list[T]) -> Result<T, :ListWasEmpty>

The last element, or :Err(:ListWasEmpty).

List.last(list())
#> :Err(:ListWasEmpty)
<T> fn(list: list[T], count: integer) -> list[T]

The first count elements, or all of them if there are fewer.

List.take_first(list(1L, 2L, 3L), 2L)
#> list(1L, 2L)
<T> fn(list: list[T], count: integer) -> list[T]

The last count elements.

List.take_last(list(1L, 2L, 3L), 2L)
#> list(2L, 3L)
<T> fn(list: list[T], count: integer) -> list[T]

Everything but the first count elements.

List.drop_first(list(1L, 2L, 3L), 2L)
#> list(3L)
<T> fn(list: list[T], count: integer) -> list[T]

Everything but the last count elements.

List.drop_last(list(1L, 2L, 3L), 2L)
#> list(1L)
<T> fn(list: list[T], index: integer) -> list[T]

Removes the element at a position.

List.drop_at(list("a", "b", "c"), 2L)
#> list("a", "c")
<T> fn(list: list[T], start: integer, count: integer) -> list[T]

count elements starting at position start.

List.sublist(list(1L, 2L, 3L, 4L), 2L, 2L)
#> list(2L, 3L)
<T> fn(list: list[T], count: integer) -> Record{before: list[T], others: list[T]}

The first count elements as before and the rest as others.

List.split_at(list(1L, 2L, 3L), 1L)
#> (before = list(1L), others = list(2L, 3L))
<T> fn(list: list[T], delimiter: T) -> Result<Record{before: list[T], after: list[T]}, :NotFound>

Splits the list around the first element identical to delimiter.

List.split_first(list("a", "-", "b", "-", "c"), "-")
#> :Ok((after = list("b", "-", "c"), before = list("a")))
<T> fn(list: list[T], delimiter: T) -> Result<Record{before: list[T], after: list[T]}, :NotFound>

Splits the list around the last element identical to delimiter.

List.split_last(list("a", "-", "b", "-", "c"), "-")
#> :Ok((after = list("c"), before = list("a", "-", "b")))
<T> fn(list: list[T], delimiter: T) -> list[list[T]]

The runs of elements between elements identical to delimiter.

List.split_on(list(1L, 0L, 2L, 3L, 0L, 4L), 0L)
#> list(list(1L), list(2L, 3L), list(4L))
<T> fn(list: list[T], delimiter: list[T]) -> list[list[T]]

The runs of elements between occurrences of the sublist delimiter.

List.split_on_list(list(1L, 0L, 0L, 2L, 0L, 3L), list(0L, 0L))
#> list(list(1L), list(2L, 0L, 3L))
<T> fn(list: list[T], size: integer) -> list[list[T]]

Groups of size elements; the last group may be shorter.

List.chunks_of(list(1L, 2L, 3L), 2L)
#> list(list(1L, 2L), list(3L))
<T> fn(list: list[T], separator: T) -> list[T]

Puts separator between every two elements.

List.intersperse(list("a", "b", "c"), "-")
#> list("a", "-", "b", "-", "c")
<T> fn(list: list[T], compare: fn(T, T) -> :LT | :EQ | :GT) -> list[T]

A stable sort by compare, which returns :LT, :EQ or :GT.

List.sort_with(list(3L, 1L, 2L), Num.compare)
#> list(1L, 2L, 3L)
<T: scalar numeric> fn(list: list[T]) -> list[T]

Sorts numbers from smallest to largest.

List.sort_asc(list(3L, 1L, 2L))
#> list(1L, 2L, 3L)
<T: scalar numeric> fn(list: list[T]) -> list[T]

Sorts numbers from largest to smallest.

List.sort_desc(list(1.5, 3, 2))
#> list(3, 2, 1.5)
<T: scalar numeric> fn(list: list[T]) -> T

The sum of the elements; 0L for an empty list. An integer sum that overflows is an error, so sum doubles, or fold with Num.add_checked to get :Err(:Overflow) instead. A double sum is accumulated like R’s sum(), with more precision than a plain left-to-right fold, so List.sum(xs) and sum(xs) give the same number.

List.sum(list(1L, 2L, 3L))
#> 6L
<T: scalar numeric> fn(list: list[T]) -> T

The product of the elements; 1L for an empty list. An integer product that overflows is an error, as for List.sum.

List.product(list(2, 2.5))
#> 5
<T: scalar numeric> fn(list: list[T]) -> Result<T, :ListWasEmpty>

The smallest element, or :Err(:ListWasEmpty).

List.min(list(3L, 1L, 2L))
#> :Ok(1L)
<T: scalar numeric> fn(list: list[T]) -> Result<T, :ListWasEmpty>

The largest element, or :Err(:ListWasEmpty).

List.max(list())
#> :Err(:ListWasEmpty)
<T> fn(list: list[T], prefix: list[T]) -> logical

Whether the list begins with the elements of prefix.

List.starts_with(list(1L, 2L, 3L), list(1L, 2L))
#> TRUE
<T> fn(list: list[T], suffix: list[T]) -> logical

Whether the list ends with the elements of suffix.

List.ends_with(list(1L, 2L, 3L), list(2L))
#> FALSE

Functions over single strings. Patterns and separators are matched literally, never as regular expressions.

fn(string: character) -> logical

Whether the string is "".

Str.is_empty("")
#> TRUE
fn(first: character, second: character) -> character

first followed by second.

Str.concat("snake", "_case")
#> "snake_case"
fn(strings: list[character], separator: character) -> character

Joins a list of strings with separator between them.

Str.join_with(list("a", "b", "c"), ", ")
#> "a, b, c"
fn(string: character, separator: character) -> list[character]

Splits at every occurrence of separator.

Str.split_on("a,b,,c", ",")
#> list("a", "b", "", "c")
fn(string: character, count: integer) -> character

count copies of the string.

Str.repeat("ab", 3L)
#> "ababab"
fn(string: character, prefix: character) -> logical

Whether the string begins with prefix.

Str.starts_with("report.csv", "report")
#> TRUE
fn(string: character, suffix: character) -> logical

Whether the string ends with suffix.

Str.ends_with("report.csv", ".R")
#> FALSE
fn(string: character, part: character) -> logical

Whether part occurs in the string, matched literally.

Str.contains("a.b", ".")
#> TRUE
fn(string: character) -> character

Removes leading and trailing whitespace.

Str.trim(" hi ")
#> "hi"
fn(string: character) -> character

Removes leading whitespace.

Str.trim_start(" hi ")
#> "hi "
fn(string: character) -> character

Removes trailing whitespace.

Str.trim_end(" hi ")
#> " hi"
fn(string: character, prefix: character) -> character

Removes prefix if the string begins with it.

Str.drop_prefix("v1.2", "v")
#> "1.2"
fn(string: character, suffix: character) -> character

Removes suffix if the string ends with it.

Str.drop_suffix("data.csv", ".csv")
#> "data"
fn(string: character, prefix: character) -> character

Puts prefix in front of the string.

Str.with_prefix("world", "hello ")
#> "hello world"
fn(string: character, pattern: character, replacement: character) -> character

Replaces every occurrence of pattern, matched literally.

Str.replace_each("a.b.c", ".", "-")
#> "a-b-c"
fn(string: character, pattern: character, replacement: character) -> character

Replaces the first occurrence of pattern.

Str.replace_first("a.b.c", ".", "-")
#> "a-b.c"
fn(string: character, pattern: character, replacement: character) -> character

Replaces the last occurrence of pattern.

Str.replace_last("a.b.c", ".", "-")
#> "a.b-c"
fn(string: character, separator: character) -> Result<Record{before: character, after: character}, :NotFound>

Splits at the first separator into before and after, or :Err(:NotFound).

Str.split_first("key=value=more", "=")
#> :Ok((after = "value=more", before = "key"))
fn(string: character, separator: character) -> Result<Record{before: character, after: character}, :NotFound>

Splits at the last separator.

Str.split_last("key=value=more", "=")
#> :Ok((after = "more", before = "key=value"))
fn(string: character) -> integer

The number of bytes in the string’s UTF-8 encoding.

Str.count_utf8_bytes("hé")
#> 3L
fn(string: character) -> list[integer]

The bytes of the string’s UTF-8 encoding.

Str.to_utf8("hé")
#> list(104L, 195L, 169L)
fn(bytes: list[integer]) -> Result<character, :BadUtf8>

The string a list of bytes encodes, or :Err(:BadUtf8).

Str.from_utf8(list(104L, 105L))
#> :Ok("hi")
fn(bytes: list[integer]) -> character

Decodes UTF-8 bytes, replacing each invalid sequence with U+FFFD.

Str.from_utf8_lossy(list(104L, 255L, 105L))
#> "h�i"
fn(string: character) -> Result<integer, :InvalidNumStr>

Parses a whole number that fits an integer, or :Err(:InvalidNumStr).

Str.to_integer("4.2")
#> :Err(:InvalidNumStr)
fn(string: character) -> Result<double, :InvalidNumStr>

Parses a decimal number, or :Err(:InvalidNumStr); NA and NaN are not numbers.

Str.to_double("4.5e1")
#> :Ok(45)
fn(string: character) -> character

Lowercases the ASCII letters and leaves every other character alone.

Str.with_ascii_lowercased("Straße ABC")
#> "straße abc"
fn(string: character) -> character

Uppercases the ASCII letters.

Str.with_ascii_uppercased("straße abc")
#> "STRAßE ABC"
fn(first: character, second: character) -> logical

Whether two strings are equal when ASCII case is ignored.

Str.caseless_ascii_equals("Content-Type", "content-type")
#> TRUE
<S> fn(string: character, state: S, f: fn(S, integer) -> S) -> S

Folds the string’s UTF-8 bytes: f(state, byte) gives the next state.

Str.walk_utf8("abc", 0L, function(total, byte) total + byte)
#> 294L
<S> fn(string: character, state: S, f: fn(S, integer, integer) -> S) -> S

Folds the string’s UTF-8 bytes, passing f each byte’s 1-based position too.

Str.walk_utf8_with_index("ab", list(), function(seen, byte, i) List.append(seen, byte * i))
#> list(97L, 196L)
fn(string: character) -> list[integer]

The string’s UTF-16 code units, each an integer from 0 to 65535. A character outside the Basic Multilingual Plane takes two units, a surrogate pair.

Str.to_utf16("h\U0001F600")
#> list(104L, 55357L, 56832L)
fn(units: list[integer]) -> Result<character, :BadUtf16>

Decodes UTF-16 code units, or :Err(:BadUtf16) for an unpaired surrogate or a zero unit, which an R string cannot hold. A number outside 0 to 65535 is an error.

Str.from_utf16(list(104L, 55357L))
#> :Err(:BadUtf16)
fn(units: list[integer]) -> character

Decodes UTF-16 code units, replacing each unpaired surrogate and each zero unit with U+FFFD.

Str.from_utf16_lossy(list(104L, 105L))
#> "hi"
fn(string: character) -> list[integer]

The string’s Unicode code points, one per character.

Str.to_utf32("h\u00e9")
#> list(104L, 233L)
fn(code_points: list[integer]) -> Result<character, :BadUtf32>

Builds a string from Unicode code points, or :Err(:BadUtf32) for a number that is not one (a surrogate, a negative number, or one above 1114111) and for zero.

Str.from_utf32(list(55296L))
#> :Err(:BadUtf32)
fn(code_points: list[integer]) -> character

Builds a string from Unicode code points, replacing each number that is not one with U+FFFD.

Str.from_utf32_lossy(list(82L, 121L))
#> "Ry"

Functions over single numbers, integer or double, and eight constants (Num.pi, Num.tau, Num.e, Num.infinity and the integer and double limits). A checked operation returns the failure as a tag where R would return NA, NaN or Inf with a warning; its unchecked twin (Num.add beside Num.add_checked) stops with an error in that case. A NaN result, such as Inf - Inf or Num.sin(Inf), is :Err(:Undefined) from a checked operation and an error from any other. The bit operations take integers as 32-bit two’s complement.

<T: scalar numeric> fn(number: T) -> character

The number as text, with up to 15 significant digits.

Num.to_str(1 / 3)
#> "0.333333333333333"
<T: scalar numeric> fn(first: T, second: T) -> :LT | :EQ | :GT

:LT, :EQ or :GT, comparing first with second.

Num.compare(2L, 1L)
#> :GT
<T: scalar numeric> fn(first: T, second: T) -> T

The smaller number.

Num.min(2L, 1L)
#> 1L
<T: scalar numeric> fn(first: T, second: T) -> T

The larger number.

Num.max(2.5, 1)
#> 2.5
<T: scalar numeric> fn(first: T, second: T) -> logical

Whether first < second.

Num.is_lt(1L, 2L)
#> TRUE
<T: scalar numeric> fn(first: T, second: T) -> logical

Whether first > second.

Num.is_gt(1L, 2L)
#> FALSE
<T: scalar numeric> fn(first: T, second: T) -> logical

Whether first <= second.

Num.is_lte(2, 2L)
#> TRUE
<T: scalar numeric> fn(first: T, second: T) -> logical

Whether first >= second.

Num.is_gte(1.5, 2)
#> FALSE
<T: scalar numeric> fn(first: T, second: T, [rtol]: double, [atol]: double) -> logical

Whether two numbers are equal within a relative tolerance rtol (default 1e-5) or an absolute tolerance atol (default 1e-8).

Num.is_approx_eq(0.1 + 0.2, 0.3)
#> TRUE
<T: scalar numeric> fn(number: T) -> logical

Whether the number is zero.

Num.is_zero(-0)
#> TRUE
<T: scalar numeric> fn(number: T) -> logical

Whether the number is greater than zero.

Num.is_positive(0L)
#> FALSE
<T: scalar numeric> fn(number: T) -> logical

Whether the number is less than zero.

Num.is_negative(-1)
#> TRUE
<T: scalar numeric> fn(number: T) -> logical

Whether the number is a whole, even number.

Num.is_even(4L)
#> TRUE
<T: scalar numeric> fn(number: T) -> logical

Whether the number is a whole, odd number.

Num.is_odd(-3L)
#> TRUE
<T: scalar numeric> fn(first: T, second: T) -> logical

Whether first is a whole multiple of second. Only zero is a multiple of zero.

Num.is_multiple_of(12L, 4L)
#> TRUE
<T: scalar numeric> fn(number: T) -> logical

Whether the number is neither infinite nor NaN.

Num.is_finite(1 / 0)
#> FALSE
<T: scalar numeric> fn(number: T) -> logical

Whether the number is Inf or -Inf.

Num.is_infinite(-1 / 0)
#> TRUE
<T: scalar numeric> fn(number: T) -> T

The absolute value.

Num.abs(-3L)
#> 3L
<T: scalar numeric> fn(first: T, second: T) -> T

The absolute difference of two numbers. An integer result that overflows is an error.

Num.abs_diff(3L, 10L)
#> 7L
<T: scalar numeric> fn(number: T) -> T

The number with its sign flipped.

Num.neg(2.5)
#> -2.5
<T: scalar numeric> fn(number: T) -> T

Rounds up to a whole number, keeping the number type.

Num.ceiling(1.2)
#> 2
<T: scalar numeric> fn(number: T) -> T

Rounds down to a whole number, keeping the number type.

Num.floor(-1.2)
#> -2
<T: scalar numeric> fn(number: T) -> T

Rounds to the nearest whole number, halves away from zero, keeping the number type.

Num.round(2.5)
#> 3
<T: scalar numeric> fn(base: T, exponent: T) -> double

base raised to exponent. A result that is not a real number, as for Num.pow(-1, 0.5), is an error.

Num.pow(2L, 10L)
#> 1024
<T: scalar numeric> fn(number: T) -> double

The sine of an angle in radians. An infinite angle is an error, because its sine is undefined; the same holds for Num.cos and Num.tan.

Num.sin(0)
#> 0
<T: scalar numeric> fn(number: T) -> double

The cosine of an angle in radians.

Num.cos(0)
#> 1
<T: scalar numeric> fn(number: T) -> double

The tangent of an angle in radians.

Num.tan(0)
#> 0
<T: scalar numeric> fn(number: T) -> double

The arctangent, in radians.

Num.atan(1) * 4
#> 3.14159265358979
<T: scalar numeric> fn(first: T, second: T) -> Result<T, :Overflow | :Undefined>

The sum, or :Err(:Overflow); a NaN sum such as Inf + -Inf is :Err(:Undefined).

Num.add_checked(.Machine$integer.max, 1L)
#> :Err(:Overflow)
<T: scalar numeric> fn(first: T, second: T) -> Result<T, :Overflow | :Undefined>

The difference, or :Err(:Overflow).

Num.sub_checked(10L, 3L)
#> :Ok(7L)
<T: scalar numeric> fn(first: T, second: T) -> Result<T, :Overflow | :Undefined>

The product, or :Err(:Overflow); a finite double that would become infinite overflows too.

Num.mul_checked(1e308, 10)
#> :Err(:Overflow)
<T: scalar numeric> fn(first: T, second: T) -> T

Adds two numbers, clamping an integer result to the largest or smallest integer.

Num.add_saturated(2147483647L, 1L)
#> 2147483647L
<T: scalar numeric> fn(first: T, second: T) -> T

Subtracts second from first, clamping an integer result.

Num.sub_saturated(-2147483647L, 1L)
#> -2147483647L
<T: scalar numeric> fn(first: T, second: T) -> T

Multiplies two numbers, clamping an integer result.

Num.mul_saturated(100000L, 100000L)
#> 2147483647L
<T: scalar numeric> fn(first: T, second: T) -> Result<double, :DivByZero | :Undefined>

The quotient as a double, or :Err(:DivByZero).

Num.div_checked(7L, 2L)
#> :Ok(3.5)
<T: scalar numeric> fn(first: T, second: T) -> Result<T, :DivByZero | :Undefined>

The quotient rounded toward zero, or :Err(:DivByZero); Inf divided by Inf is :Err(:Undefined).

Num.div_trunc_checked(-7L, 2L)
#> :Ok(-3L)
Num.div_trunc_checked(Inf, Inf)
#> :Err(:Undefined)
<T: scalar numeric> fn(first: T, second: T) -> Result<T, :DivByZero | :Undefined>

Divides and rounds up, or :Err(:DivByZero).

Num.div_ceil_checked(7L, 2L)
#> :Ok(4L)
<T: scalar numeric> fn(first: T, second: T) -> Result<T, :DivByZero | :Undefined>

The remainder of the truncated division, with the sign of first, or :Err(:DivByZero).

Num.rem_checked(-7L, 2L)
#> :Ok(-1L)
<T: scalar numeric> fn(number: T) -> Result<double, :SqrtOfNegative>

The square root, or :Err(:SqrtOfNegative).

Num.sqrt_checked(-1)
#> :Err(:SqrtOfNegative)
<T: scalar numeric> fn(number: T) -> Result<double, :LogNeedsPositive>

The natural logarithm, or :Err(:LogNeedsPositive).

Num.log_checked(exp(2))
#> :Ok(2)
<T: scalar numeric> fn(number: T) -> double

The number as a double.

Num.to_double(3L)
#> 3
<T: scalar numeric> fn(number: T) -> Result<integer, :OutOfBounds>

The number truncated toward zero as an integer, or :Err(:OutOfBounds) when it does not fit.

Num.to_integer_checked(-3.7)
#> :Ok(-3L)
<T: scalar numeric> fn(number: T) -> integer

The number truncated toward zero as an integer. A number that does not fit an integer is an error; Num.to_integer_checked returns :Err(:OutOfBounds) instead.

Num.to_integer(-3.7)
#> -3L
<T: scalar numeric> fn(first: T, second: T) -> T

The sum. An integer sum that overflows stops with an error, so use Num.add_checked when overflow is possible. Unlike +, it can be passed by name.

List.walk(list(1L, 2L, 3L), 0L, Num.add)
#> 6L
<T: scalar numeric> fn(first: T, second: T) -> T

second subtracted from first. An integer result that overflows is an error, and so is a NaN result such as Inf - Inf.

Num.sub(10L, 4L)
#> 6L
<T: scalar numeric> fn(first: T, second: T) -> T

The product. An integer product that overflows is an error.

Num.mul(6L, 7L)
#> 42L
<T: scalar numeric> fn(first: T, second: T) -> double

The quotient as a double. A zero divisor is an error; Num.div_checked returns :Err(:DivByZero) instead.

Num.div(7L, 2L)
#> 3.5
<T: scalar numeric> fn(first: T, second: T) -> T

The quotient rounded toward zero. A zero divisor is an error.

Num.div_trunc(-7L, 2L)
#> -3L
<T: scalar numeric> fn(first: T, second: T) -> T

The quotient rounded up. A zero divisor is an error.

Num.div_ceil(7L, 2L)
#> 4L
<T: scalar numeric> fn(first: T, second: T) -> T

The remainder of the truncated division, with the sign of first. A zero divisor is an error.

Num.rem(-7L, 2L)
#> -1L
fn(base: integer, exponent: integer) -> integer

base raised to a non-negative exponent, as an integer. A result that overflows is an error; Num.pow returns a double instead.

Num.pow_int(3L, 4L)
#> 81L
<T: scalar numeric> fn(number: T) -> double

The square root. A negative number is an error; Num.sqrt_checked returns :Err(:SqrtOfNegative) instead.

Num.sqrt(16L)
#> 4
<T: scalar numeric> fn(number: T) -> double

The natural logarithm. A number that is not positive is an error; Num.log_checked returns :Err(:LogNeedsPositive) instead.

Num.log(Num.e)
#> 1
<T: scalar numeric> fn(number: T) -> double

The arcsine in radians. A number outside -1 to 1 is an error.

Num.asin(1)
#> 1.5707963267949
<T: scalar numeric> fn(number: T) -> double

The arccosine in radians. A number outside -1 to 1 is an error.

Num.acos(1)
#> 0
<T: scalar numeric> fn(number: T) -> logical

Whether the number is NaN. It is the one library function that accepts NaN; NA is still an error.

Num.is_nan(0 / 0)
#> TRUE
fn(first: integer, second: integer) -> integer

The bits set in both integers, in 32-bit two’s complement.

Num.bitwise_and(12L, 10L)
#> 8L
fn(first: integer, second: integer) -> integer

The bits set in either integer.

Num.bitwise_or(12L, 10L)
#> 14L
fn(first: integer, second: integer) -> integer

The bits set in exactly one of the integers. A result whose bit pattern is -2147483648 is an error, because R uses that value for NA; the same holds for every bit operation.

Num.bitwise_xor(12L, 10L)
#> 6L
fn(number: integer) -> integer

The integer with every bit flipped, which is -number - 1.

Num.bitwise_not(5L)
#> -6L
fn(number: integer, bits: integer) -> integer

Shifts the bits left by bits, from 0 to 31. Bits shifted past the top are lost, as in Roc.

Num.shift_left_by(1L, 4L)
#> 16L
fn(number: integer, bits: integer) -> integer

Shifts the bits right by bits, copying the sign bit in, so a negative number stays negative.

Num.shift_right_by(-16L, 2L)
#> -4L
fn(number: integer, bits: integer) -> integer

Shifts the bits right by bits, filling with zeros.

Num.shift_right_zf_by(-16L, 28L)
#> 15L
fn(number: integer) -> integer

The number of set bits.

Num.count_one_bits(255L)
#> 8L
fn(number: integer) -> integer

The number of zero bits above the highest set bit, 32 for zero.

Num.count_leading_zero_bits(1L)
#> 31L
fn(number: integer) -> integer

The number of zero bits below the lowest set bit, 32 for zero.

Num.count_trailing_zero_bits(8L)
#> 3L
double

The ratio of a circle’s circumference to its diameter. It is a value, not a function.

Num.pi
#> 3.14159265358979
double

The ratio of a circle’s circumference to its radius, 2 * Num.pi.

Num.tau
#> 6.28318530717959
double

Euler’s number, the base of the natural logarithm.

Num.e
#> 2.71828182845905
integer

The largest integer, 2147483647L. It is a value, not a function.

Num.max_integer
#> 2147483647L
integer

The smallest integer, -2147483647L. R stores NA in the one value below it, so that value is not an integer here.

Num.min_integer
#> -2147483647L
double

The largest finite double.

Num.max_double
#> 1.79769313486232e+308
double

The most negative finite double, the negation of Num.max_double.

Num.min_double
#> -1.79769313486232e+308
double

Positive infinity, R’s Inf.

c(Num.infinity, -Num.infinity)
#> c(Inf, -Inf)

Booleans are R’s TRUE and FALSE, never NA. R’s operators already cover most of this module; its functions exist so that a boolean step can be passed by name, as in List.walk(flags, Bool.true, Bool.and), and so that code written against Roc’s names reads the same.

logical

TRUE, as Roc spells it. It is a value, not a function.

Bool.true
#> TRUE
logical

FALSE.

Bool.false
#> FALSE
fn(first: logical, second: logical) -> logical

Whether both are TRUE. Both arguments are evaluated, unlike with &&.

Bool.and(TRUE, FALSE)
#> FALSE
fn(first: logical, second: logical) -> logical

Whether either is TRUE. Both arguments are evaluated, unlike with ||.

Bool.or(TRUE, FALSE)
#> TRUE
fn(value: logical) -> logical

The opposite value.

List.keep_if(list(1L, 2L, 3L), function(x) Bool.not(Num.is_even(x)))
#> list(1L, 3L)
<T> fn(first: T, second: T) -> logical

Whether two values are equal by identical(), the library’s equality. An integer and a double are never identical, so Bool.is_eq(1L, 1) is FALSE, and a dictionary or set compares without regard to order.

Bool.is_eq(Set.from_list(list(1L, 2L)), Set.from_list(list(2L, 1L)))
#> TRUE
<T> fn(first: T, second: T) -> logical

Whether two values differ by identical().

Bool.is_not_eq(list(1L), list(2L))
#> TRUE

A dictionary maps keys to values, typed Dict<K, V>. See Dictionaries and sets for the rules on keys.

<K: key, V> fn() -> Dict<K, V>

A dictionary with no entries.

Dict.empty()
#> Dict.empty()
<K: key, V> fn(capacity: integer) -> Dict<K, V>

An empty dictionary with room for capacity entries, so inserting that many never grows it. The room is rounded up to a power of two.

Dict.capacity(Dict.insert(Dict.with_capacity(64L), "a", 1L))
#> 64L
<K: key, V> fn(key: K, value: V) -> Dict<K, V>

A dictionary with one entry.

Dict.single("a", 1)
#> Dict.from_list(list(("a", 1)))
<K: key, V> fn(pairs: list[Tuple{K, V}]) -> Dict<K, V>

A dictionary from (key, value) tuples; a later tuple replaces an earlier one with the same key.

Dict.from_list(list(("a", 1), ("b", 2), ("a", 3)))
#> Dict.from_list(list(("a", 3), ("b", 2)))
<K: key, V> fn(dict: Dict<K, V>) -> list[Tuple{K, V}]

The entries as (key, value) tuples, in insertion order.

Dict.to_list(Dict.from_list(list(("a", 1), ("b", 2))))
#> list(("a", 1), ("b", 2))
<K: key, V> fn(dict: Dict<K, V>) -> integer

The number of entries.

Dict.len(Dict.single("a", 1))
#> 1L
<K: key, V> fn(dict: Dict<K, V>) -> logical

Whether the dictionary has no entries.

Dict.is_empty(Dict.empty())
#> TRUE
<K: key, V> fn(dict: Dict<K, V>, key: K) -> Result<V, :KeyNotFound>

The value for key, or :Err(:KeyNotFound).

Dict.get(Dict.single("a", 1), "a")
#> :Ok(1)
<K: key, V> fn(dict: Dict<K, V>, key: K) -> logical

Whether the dictionary has an entry for key.

Dict.contains(Dict.single("a", 1), "b")
#> FALSE
<K: key, V> fn(dict: Dict<K, V>, key: K, value: V) -> Dict<K, V>

Adds an entry, or replaces the value of an existing key in place.

Dict.insert(Dict.single("a", 1), "b", 2)
#> Dict.from_list(list(("a", 1), ("b", 2)))
<K: key, V> fn(dict: Dict<K, V>, key: K) -> Dict<K, V>

Removes the entry for key, if there is one.

Dict.remove(Dict.from_list(list(("a", 1), ("b", 2))), "a")
#> Dict.from_list(list(("b", 2)))
<K: key, V> fn(dict: Dict<K, V>, key: K, f: fn(Result<V, :Missing>) -> Result<V, :Missing>) -> Dict<K, V>

Sets the entry for key from f, which gets :Ok(value) or :Err(:Missing) and returns either; :Err removes the entry.

Dict.update(Dict.single("hits", 1L), "hits", function(r) :Ok(Result.with_default(r, 0L) + 1L))
#> Dict.from_list(list(("hits", 2L)))
<K: key, V> fn(dict: Dict<K, V>) -> list[K]

The keys, in insertion order.

Dict.keys(Dict.from_list(list(("a", 1), ("b", 2))))
#> list("a", "b")
<K: key, V> fn(dict: Dict<K, V>) -> list[V]

The values, in insertion order.

Dict.values(Dict.from_list(list(("a", 1), ("b", 2))))
#> list(1, 2)
<K: key, V, S> fn(dict: Dict<K, V>, state: S, f: fn(S, K, V) -> S) -> S

Folds over the entries: f gets the state, a key and its value.

Dict.walk(Dict.from_list(list(("a", 1), ("b", 2))), 0, function(total, k, v) total + v)
#> 3
<K: key, V, S> fn(dict: Dict<K, V>, state: S, f: fn(S, K, V) -> :Continue(S) | :Break(S)) -> S

Folds the entries until f returns :Break(state).

Dict.walk_until(Dict.from_list(list(("a", 1L), ("b", 5L), ("c", 2L))), 0L, function(total, k, v) if (v > 4L) :Break(total) else :Continue(total + v))
#> 1L
<K: key, A, B> fn(dict: Dict<K, A>, f: fn(K, A) -> B) -> Dict<K, B>

Replaces every value with f of its key and value.

Dict.map(Dict.from_list(list(("a", 1), ("b", 2))), function(k, v) v * 10)
#> Dict.from_list(list(("a", 10), ("b", 20)))
<K: key, V, X: key, Y> fn(dict: Dict<K, V>, f: fn(K, V) -> Dict<X, Y>) -> Dict<X, Y>

Unions the dictionaries f returns for each entry; a later entry’s value wins.

Dict.join_map(Dict.from_list(list(("a", 1L), ("b", 2L))), function(k, v) Dict.single(v, k))
#> Dict.from_list(list((1L, "a"), (2L, "b")))
<K: key, V> fn(dict: Dict<K, V>, predicate: fn(K, V) -> logical) -> Dict<K, V>

The entries for which predicate of the key and value is TRUE.

Dict.keep_if(Dict.from_list(list(("a", 1), ("b", 2))), function(k, v) v > 1)
#> Dict.from_list(list(("b", 2)))
<K: key, V> fn(dict: Dict<K, V>, predicate: fn(K, V) -> logical) -> Dict<K, V>

The entries for which predicate is FALSE.

Dict.drop_if(Dict.from_list(list(("a", 1), ("b", 2))), function(k, v) v > 1)
#> Dict.from_list(list(("a", 1)))
<K: key, V> fn(first: Dict<K, V>, second: Dict<K, V>) -> Dict<K, V>

The entries of both; where a key is in both, second’s value wins.

Dict.insert_all(Dict.single("a", 1), Dict.from_list(list(("a", 5), ("b", 2))))
#> Dict.from_list(list(("a", 5), ("b", 2)))
<K: key, V> fn(first: Dict<K, V>, second: Dict<K, V>) -> Dict<K, V>

The entries of first whose key and value are both in second.

Dict.keep_shared(Dict.from_list(list(("a", 1), ("b", 2))), Dict.from_list(list(("a", 1), ("b", 0))))
#> Dict.from_list(list(("a", 1)))
<K: key, V> fn(first: Dict<K, V>, second: Dict<K, V>) -> Dict<K, V>

The entries of first whose key is not in second.

Dict.remove_all(Dict.from_list(list(("a", 1), ("b", 2))), Dict.single("a", 0))
#> Dict.from_list(list(("b", 2)))
<K: key, V> fn(dict: Dict<K, V>, spare: integer) -> Dict<K, V>

The dictionary with room for spare more entries.

Dict.reserve(Dict.single("a", 1L), 1000L)
#> Dict.from_list(list(("a", 1L)))
<K: key, V> fn(dict: Dict<K, V>) -> integer

How many entries the dictionary holds before its storage must grow.

Dict.capacity(Dict.reserve(Dict.single("a", 1L), 9L))
#> 10L
<K: key, V> fn(dict: Dict<K, V>) -> Dict<K, V>

The dictionary with no room beyond its entries. An empty dictionary keeps its smallest index, which has room for 4.

Dict.capacity(Dict.release_excess_capacity(Dict.reserve(Dict.single("a", 1L), 9L)))
#> 1L
<K: key, V> fn(dict: Dict<K, V>) -> Dict<K, V>

An empty dictionary of the same type.

Dict.clear(Dict.single("a", 1L))
#> Dict.empty()

A set of distinct keys, typed Set<T>.

<T: key> fn() -> Set<T>

A set with no elements.

Set.empty()
#> Set.empty()
<T: key> fn(capacity: integer) -> Set<T>

An empty set with room for capacity elements, so inserting that many never grows it. The room is rounded up to a power of two.

Set.capacity(Set.insert(Set.with_capacity(64L), "a"))
#> 64L
<T: key> fn(element: T) -> Set<T>

A set with one element.

Set.single("a")
#> Set.from_list(list("a"))
<T: key> fn(list: list[T]) -> Set<T>

A set of the list’s elements, without duplicates, in first-seen order.

Set.from_list(list(2L, 1L, 2L))
#> Set.from_list(list(2L, 1L))
<T: key> fn(set: Set<T>) -> list[T]

The elements, in insertion order.

Set.to_list(Set.from_list(list("b", "a")))
#> list("b", "a")
<T: key> fn(set: Set<T>) -> integer

The number of elements.

Set.len(Set.from_list(list(1L, 1L, 2L)))
#> 2L
<T: key> fn(set: Set<T>) -> logical

Whether the set has no elements.

Set.is_empty(Set.empty())
#> TRUE
<T: key> fn(set: Set<T>, element: T) -> logical

Whether element is in the set.

Set.contains(Set.from_list(list(1L, 2L)), 2L)
#> TRUE
<T: key> fn(set: Set<T>, element: T) -> Set<T>

Adds an element.

Set.insert(Set.single(1L), 2L)
#> Set.from_list(list(1L, 2L))
<T: key> fn(set: Set<T>, element: T) -> Set<T>

Removes an element, if it is there.

Set.remove(Set.from_list(list(1L, 2L)), 1L)
#> Set.from_list(list(2L))
<T: key> fn(first: Set<T>, second: Set<T>) -> Set<T>

The elements of either set.

Set.union(Set.from_list(list(1L, 2L)), Set.from_list(list(2L, 3L)))
#> Set.from_list(list(1L, 2L, 3L))
<T: key> fn(first: Set<T>, second: Set<T>) -> Set<T>

The elements of both sets.

Set.intersection(Set.from_list(list(1L, 2L)), Set.from_list(list(2L, 3L)))
#> Set.from_list(list(2L))
<T: key> fn(first: Set<T>, second: Set<T>) -> Set<T>

The elements of first that are not in second.

Set.difference(Set.from_list(list(1L, 2L)), Set.from_list(list(2L, 3L)))
#> Set.from_list(list(1L))
<T: key, S> fn(set: Set<T>, state: S, f: fn(S, T) -> S) -> S

Folds over the elements.

Set.walk(Set.from_list(list(1L, 2L, 3L)), 0L, function(total, x) total + x)
#> 6L
<T: key, S> fn(set: Set<T>, state: S, f: fn(S, T) -> :Continue(S) | :Break(S)) -> S

Folds the elements until f returns :Break(state).

Set.walk_until(Set.from_list(list(1L, 2L, 3L)), 0L, function(total, x) if (x > 2L) :Break(total) else :Continue(total + x))
#> 3L
<A: key, B: key> fn(set: Set<A>, f: fn(A) -> B) -> Set<B>

The set of f of every element.

Set.map(Set.from_list(list(1L, 2L, 3L)), function(x) x %% 2L)
#> Set.from_list(list(1L, 0L))
<A: key, B: key> fn(set: Set<A>, f: fn(A) -> Set<B>) -> Set<B>

Unions the sets f returns for each element.

Set.join_map(Set.from_list(list(1L, 2L)), function(x) Set.from_list(list(x, x * 10L)))
#> Set.from_list(list(1L, 10L, 2L, 20L))
<T: key> fn(set: Set<T>, predicate: fn(T) -> logical) -> Set<T>

The elements for which predicate is TRUE.

Set.keep_if(Set.from_list(list(1L, 2L, 3L)), function(x) x > 1L)
#> Set.from_list(list(2L, 3L))
<T: key> fn(set: Set<T>, predicate: fn(T) -> logical) -> Set<T>

The elements for which predicate is FALSE.

Set.drop_if(Set.from_list(list(1L, 2L, 3L)), function(x) x > 1L)
#> Set.from_list(list(1L))
<T: key> fn(set: Set<T>, spare: integer) -> Set<T>

The set with room for spare more elements.

Set.reserve(Set.single("a"), 1000L)
#> Set.from_list(list("a"))
<T: key> fn(set: Set<T>) -> integer

How many elements the set holds before its storage must grow.

Set.capacity(Set.reserve(Set.single("a"), 9L))
#> 10L
<T: key> fn(set: Set<T>) -> Set<T>

The set with no room beyond its elements. An empty set keeps its smallest index, which has room for 4.

Set.capacity(Set.release_excess_capacity(Set.reserve(Set.single("a"), 9L)))
#> 1L