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"))#: fn(text: character) -> Result<list[integer], :InvalidNumStr>parse_ids <- function(text) { parts <- List.map(Str.split_on(text, ","), Str.trim) List.map_try(parts, Str.to_integer)}
#: Dict<character, integer>counts <- 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` insteadPlain Rscript knows nothing of ry, so there the call fails with “could not find function”.
Conventions
Section titled “Conventions”These rules hold in every module:
- Names are plain R names.
List.mapis one symbol, called like any R function; there is no method syntax. The names live in ry’srypackage, attached at startup aspackage:ry, so a definition of your own, or one from a package you attach, comes first, andry::List.mapstill reaches the library’s function. - A collection is an R list.
List.maptakes any list and returns a new one, andStr.split_onreturns a list of strings.List.from_vectorturns an atomic vector into a list, andList.to_vectorturns a list of scalars back. - Positions are 1-based
integers, as inxs[[1L]]. A whole double such as2is accepted too. - A string is a length-one
character, and a number a length-oneintegerordouble.NAandNaNare refused with an error, because no function has anNAcase to fall into. The one exception isNum.is_nan, which acceptsNaNbecause telling it apart is its job. - Misuse is an error, not a tag. An argument of the wrong type, an
NA, or anintegerList.sumorList.productthat overflows stops with an R error that names the problem. So does an uncheckedNumoperation whose result does not exist, such asNum.div(1, 0), where Roc would crash; its_checkedvariant returns the failure as a tag instead. - No function returns
NaN. A result that would beNaN, asNum.sin(Inf),Num.pow(-1, 0.5)andList.sum(list(Inf, -Inf))would be, is an error, and a_checkedfunction returns:Err(:Undefined)for it instead. - Failure is a tag. A function that can fail returns
:Ok(value)or:Err(tag), typedResult<T, E>, and the tag names the reason::OutOfBounds,:NotFound,:ListWasEmpty,:KeyNotFound,:InvalidNumStr,:BadUtf8,:BadUtf16,:BadUtf32,:Overflow,:DivByZero,:Undefined,:SqrtOfNegativeor:LogNeedsPositive. - Equality is
identical(), wherever a function compares values (List.contains,Dict.keep_shared). - Orderings are tags.
Num.comparereturns:LT,:EQor:GT, andList.sort_withtakes 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 2Dictionaries and sets
Section titled “Dictionaries and sets”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] TRUEA 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()`Updates in place
Section titled “Updates in place”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)#> 100000LWhen 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.
Checking
Section titled “Checking”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 typeInteger 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.
Reference
Section titled “Reference”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.
Result
Section titled “Result”A result is :Ok(value) or :Err(error), typed Result<T, E>. The alias is built in, so every annotation can use it.
Result.is_ok
Section titled “Result.is_ok”<T, E> fn(result: Result<T, E>) -> logicalWhether the result is :Ok.
Result.is_ok(:Ok(1L))#> TRUEResult.is_err
Section titled “Result.is_err”<T, E> fn(result: Result<T, E>) -> logicalWhether the result is :Err.
Result.is_err(:Ok(1L))#> FALSEResult.with_default
Section titled “Result.with_default”<T, E> fn(result: Result<T, E>, default: T) -> TThe :Ok value, or default for an :Err.
Result.with_default(:Err(:Missing), 0L)#> 0LResult.map
Section titled “Result.map”<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)Result.map_err
Section titled “Result.map_err”<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"))Result.map_both
Section titled “Result.map_both”<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")Result.map2
Section titled “Result.map2”<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)Result.and_then
Section titled “Result.and_then”<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)Result.on_err
Section titled “Result.on_err”<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.
List.from_vector
Section titled “List.from_vector”<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)List.to_vector
Section titled “List.to_vector”<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)List.len
Section titled “List.len”<T> fn(list: list[T]) -> integerThe number of elements.
List.len(list("a", "b"))#> 2LList.is_empty
Section titled “List.is_empty”<T> fn(list: list[T]) -> logicalWhether the list has no elements.
List.is_empty(list())#> TRUEList.get
Section titled “List.get”<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)List.set
Section titled “List.set”<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")List.update
Section titled “List.update”<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)List.swap
Section titled “List.swap”<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")List.replace
Section titled “List.replace”<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")List.append
Section titled “List.append”<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)List.append_if_ok
Section titled “List.append_if_ok”<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)List.prepend
Section titled “List.prepend”<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)List.prepend_if_ok
Section titled “List.prepend_if_ok”<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)List.concat
Section titled “List.concat”<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)List.concat_utf8
Section titled “List.concat_utf8”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)List.single
Section titled “List.single”<T> fn(value: T) -> list[T]A list of one element.
List.single("only")#> list("only")List.repeat
Section titled “List.repeat”<T> fn(value: T, count: integer) -> list[T]A list of count copies of value.
List.repeat("x", 3L)#> list("x", "x", "x")List.range
Section titled “List.range”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()List.with_capacity
Section titled “List.with_capacity”<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))#> 0LList.reserve
Section titled “List.reserve”<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)List.release_excess_capacity
Section titled “List.release_excess_capacity”<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)List.reverse
Section titled “List.reverse”<T> fn(list: list[T]) -> list[T]The elements in reverse order.
List.reverse(list(1L, 2L, 3L))#> list(3L, 2L, 1L)List.join
Section titled “List.join”<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)List.map
Section titled “List.map”<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)List.map_with_index
Section titled “List.map_with_index”<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")List.map2
Section titled “List.map2”<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)List.map3
Section titled “List.map3”<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)List.map4
Section titled “List.map4”<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)List.join_map
Section titled “List.join_map”<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)List.keep_if
Section titled “List.keep_if”<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)List.keep_if_try
Section titled “List.keep_if_try”<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)List.drop_if
Section titled “List.drop_if”<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)List.keep_oks
Section titled “List.keep_oks”<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)List.keep_errs
Section titled “List.keep_errs”<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)List.map_try
Section titled “List.map_try”<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))List.walk
Section titled “List.walk”<T, S> fn(list: list[T], state: S, f: fn(S, T) -> S) -> SFolds 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)#> 6LList.walk_backwards
Section titled “List.walk_backwards”<T, S> fn(list: list[T], state: S, f: fn(S, T) -> S) -> SFolds from the last element.
List.walk_backwards(list("a", "b", "c"), "", function(text, x) Str.concat(text, x))#> "cba"List.walk_until
Section titled “List.walk_until”<T, S> fn(list: list[T], state: S, f: fn(S, T) -> :Continue(S) | :Break(S)) -> SFolds 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))#> 3LList.walk_backwards_until
Section titled “List.walk_backwards_until”<T, S> fn(list: list[T], state: S, f: fn(S, T) -> :Continue(S) | :Break(S)) -> SFolds 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))#> 5LList.walk_with_index
Section titled “List.walk_with_index”<T, S> fn(list: list[T], state: S, f: fn(S, T, integer) -> S) -> SFolds 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)#> 140LList.walk_with_index_until
Section titled “List.walk_with_index_until”<T, S> fn(list: list[T], state: S, f: fn(S, T, integer) -> :Continue(S) | :Break(S)) -> SFolds 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"List.walk_from
Section titled “List.walk_from”<T, S> fn(list: list[T], index: integer, state: S, f: fn(S, T) -> S) -> SFolds the list from a 1-based position onwards.
List.walk_from(list(1L, 2L, 3L, 4L), 3L, 0L, function(total, x) total + x)#> 7LList.walk_from_until
Section titled “List.walk_from_until”<T, S> fn(list: list[T], index: integer, state: S, f: fn(S, T) -> :Continue(S) | :Break(S)) -> SFolds 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))#> 5LList.walk_try
Section titled “List.walk_try”<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)List.for_each
Section titled “List.for_each”<T> fn(list: list[T], f: fn(T) -> Any) -> NULLCalls f on each element for its effect, and returns NULL invisibly.
List.for_each(list("a", "b"), function(x) cat(x, "\n"))#> a#> bList.for_each_try
Section titled “List.for_each_try”<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))List.any
Section titled “List.any”<T> fn(list: list[T], predicate: fn(T) -> logical) -> logicalWhether predicate is TRUE for some element.
List.any(list(1L, 5L), function(x) x > 4L)#> TRUEList.all
Section titled “List.all”<T> fn(list: list[T], predicate: fn(T) -> logical) -> logicalWhether predicate is TRUE for every element; TRUE for an empty list.
List.all(list(1L, 5L), function(x) x > 4L)#> FALSEList.contains
Section titled “List.contains”<T> fn(list: list[T], value: T) -> logicalWhether some element is identical() to value.
List.contains(list("a", "b"), "b")#> TRUEList.count_if
Section titled “List.count_if”<T> fn(list: list[T], predicate: fn(T) -> logical) -> integerThe number of elements for which predicate is TRUE.
List.count_if(list(1L, 5L, 7L), function(x) x > 4L)#> 2LList.find_first
Section titled “List.find_first”<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)List.find_last
Section titled “List.find_last”<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)List.find_first_index
Section titled “List.find_first_index”<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)List.find_last_index
Section titled “List.find_last_index”<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)List.first
Section titled “List.first”<T> fn(list: list[T]) -> Result<T, :ListWasEmpty>The first element, or :Err(:ListWasEmpty).
List.first(list("a", "b"))#> :Ok("a")List.last
Section titled “List.last”<T> fn(list: list[T]) -> Result<T, :ListWasEmpty>The last element, or :Err(:ListWasEmpty).
List.last(list())#> :Err(:ListWasEmpty)List.take_first
Section titled “List.take_first”<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)List.take_last
Section titled “List.take_last”<T> fn(list: list[T], count: integer) -> list[T]The last count elements.
List.take_last(list(1L, 2L, 3L), 2L)#> list(2L, 3L)List.drop_first
Section titled “List.drop_first”<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)List.drop_last
Section titled “List.drop_last”<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)List.drop_at
Section titled “List.drop_at”<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")List.sublist
Section titled “List.sublist”<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)List.split_at
Section titled “List.split_at”<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))List.split_first
Section titled “List.split_first”<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")))List.split_last
Section titled “List.split_last”<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")))List.split_on
Section titled “List.split_on”<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))List.split_on_list
Section titled “List.split_on_list”<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))List.chunks_of
Section titled “List.chunks_of”<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))List.intersperse
Section titled “List.intersperse”<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")List.sort_with
Section titled “List.sort_with”<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)List.sort_asc
Section titled “List.sort_asc”<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)List.sort_desc
Section titled “List.sort_desc”<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)List.sum
Section titled “List.sum”<T: scalar numeric> fn(list: list[T]) -> TThe 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))#> 6LList.product
Section titled “List.product”<T: scalar numeric> fn(list: list[T]) -> TThe 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))#> 5List.min
Section titled “List.min”<T: scalar numeric> fn(list: list[T]) -> Result<T, :ListWasEmpty>The smallest element, or :Err(:ListWasEmpty).
List.min(list(3L, 1L, 2L))#> :Ok(1L)List.max
Section titled “List.max”<T: scalar numeric> fn(list: list[T]) -> Result<T, :ListWasEmpty>The largest element, or :Err(:ListWasEmpty).
List.max(list())#> :Err(:ListWasEmpty)List.starts_with
Section titled “List.starts_with”<T> fn(list: list[T], prefix: list[T]) -> logicalWhether the list begins with the elements of prefix.
List.starts_with(list(1L, 2L, 3L), list(1L, 2L))#> TRUEList.ends_with
Section titled “List.ends_with”<T> fn(list: list[T], suffix: list[T]) -> logicalWhether the list ends with the elements of suffix.
List.ends_with(list(1L, 2L, 3L), list(2L))#> FALSEFunctions over single strings. Patterns and separators are matched literally, never as regular expressions.
Str.is_empty
Section titled “Str.is_empty”fn(string: character) -> logicalWhether the string is "".
Str.is_empty("")#> TRUEStr.concat
Section titled “Str.concat”fn(first: character, second: character) -> characterfirst followed by second.
Str.concat("snake", "_case")#> "snake_case"Str.join_with
Section titled “Str.join_with”fn(strings: list[character], separator: character) -> characterJoins a list of strings with separator between them.
Str.join_with(list("a", "b", "c"), ", ")#> "a, b, c"Str.split_on
Section titled “Str.split_on”fn(string: character, separator: character) -> list[character]Splits at every occurrence of separator.
Str.split_on("a,b,,c", ",")#> list("a", "b", "", "c")Str.repeat
Section titled “Str.repeat”fn(string: character, count: integer) -> charactercount copies of the string.
Str.repeat("ab", 3L)#> "ababab"Str.starts_with
Section titled “Str.starts_with”fn(string: character, prefix: character) -> logicalWhether the string begins with prefix.
Str.starts_with("report.csv", "report")#> TRUEStr.ends_with
Section titled “Str.ends_with”fn(string: character, suffix: character) -> logicalWhether the string ends with suffix.
Str.ends_with("report.csv", ".R")#> FALSEStr.contains
Section titled “Str.contains”fn(string: character, part: character) -> logicalWhether part occurs in the string, matched literally.
Str.contains("a.b", ".")#> TRUEStr.trim
Section titled “Str.trim”fn(string: character) -> characterRemoves leading and trailing whitespace.
Str.trim(" hi ")#> "hi"Str.trim_start
Section titled “Str.trim_start”fn(string: character) -> characterRemoves leading whitespace.
Str.trim_start(" hi ")#> "hi "Str.trim_end
Section titled “Str.trim_end”fn(string: character) -> characterRemoves trailing whitespace.
Str.trim_end(" hi ")#> " hi"Str.drop_prefix
Section titled “Str.drop_prefix”fn(string: character, prefix: character) -> characterRemoves prefix if the string begins with it.
Str.drop_prefix("v1.2", "v")#> "1.2"Str.drop_suffix
Section titled “Str.drop_suffix”fn(string: character, suffix: character) -> characterRemoves suffix if the string ends with it.
Str.drop_suffix("data.csv", ".csv")#> "data"Str.with_prefix
Section titled “Str.with_prefix”fn(string: character, prefix: character) -> characterPuts prefix in front of the string.
Str.with_prefix("world", "hello ")#> "hello world"Str.replace_each
Section titled “Str.replace_each”fn(string: character, pattern: character, replacement: character) -> characterReplaces every occurrence of pattern, matched literally.
Str.replace_each("a.b.c", ".", "-")#> "a-b-c"Str.replace_first
Section titled “Str.replace_first”fn(string: character, pattern: character, replacement: character) -> characterReplaces the first occurrence of pattern.
Str.replace_first("a.b.c", ".", "-")#> "a-b.c"Str.replace_last
Section titled “Str.replace_last”fn(string: character, pattern: character, replacement: character) -> characterReplaces the last occurrence of pattern.
Str.replace_last("a.b.c", ".", "-")#> "a.b-c"Str.split_first
Section titled “Str.split_first”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"))Str.split_last
Section titled “Str.split_last”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"))Str.count_utf8_bytes
Section titled “Str.count_utf8_bytes”fn(string: character) -> integerThe number of bytes in the string’s UTF-8 encoding.
Str.count_utf8_bytes("hé")#> 3LStr.to_utf8
Section titled “Str.to_utf8”fn(string: character) -> list[integer]The bytes of the string’s UTF-8 encoding.
Str.to_utf8("hé")#> list(104L, 195L, 169L)Str.from_utf8
Section titled “Str.from_utf8”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")Str.from_utf8_lossy
Section titled “Str.from_utf8_lossy”fn(bytes: list[integer]) -> characterDecodes UTF-8 bytes, replacing each invalid sequence with U+FFFD.
Str.from_utf8_lossy(list(104L, 255L, 105L))#> "h�i"Str.to_integer
Section titled “Str.to_integer”fn(string: character) -> Result<integer, :InvalidNumStr>Parses a whole number that fits an integer, or :Err(:InvalidNumStr).
Str.to_integer("4.2")#> :Err(:InvalidNumStr)Str.to_double
Section titled “Str.to_double”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)Str.with_ascii_lowercased
Section titled “Str.with_ascii_lowercased”fn(string: character) -> characterLowercases the ASCII letters and leaves every other character alone.
Str.with_ascii_lowercased("Straße ABC")#> "straße abc"Str.with_ascii_uppercased
Section titled “Str.with_ascii_uppercased”fn(string: character) -> characterUppercases the ASCII letters.
Str.with_ascii_uppercased("straße abc")#> "STRAßE ABC"Str.caseless_ascii_equals
Section titled “Str.caseless_ascii_equals”fn(first: character, second: character) -> logicalWhether two strings are equal when ASCII case is ignored.
Str.caseless_ascii_equals("Content-Type", "content-type")#> TRUEStr.walk_utf8
Section titled “Str.walk_utf8”<S> fn(string: character, state: S, f: fn(S, integer) -> S) -> SFolds the string’s UTF-8 bytes: f(state, byte) gives the next state.
Str.walk_utf8("abc", 0L, function(total, byte) total + byte)#> 294LStr.walk_utf8_with_index
Section titled “Str.walk_utf8_with_index”<S> fn(string: character, state: S, f: fn(S, integer, integer) -> S) -> SFolds 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)Str.to_utf16
Section titled “Str.to_utf16”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)Str.from_utf16
Section titled “Str.from_utf16”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)Str.from_utf16_lossy
Section titled “Str.from_utf16_lossy”fn(units: list[integer]) -> characterDecodes UTF-16 code units, replacing each unpaired surrogate and each zero unit with U+FFFD.
Str.from_utf16_lossy(list(104L, 105L))#> "hi"Str.to_utf32
Section titled “Str.to_utf32”fn(string: character) -> list[integer]The string’s Unicode code points, one per character.
Str.to_utf32("h\u00e9")#> list(104L, 233L)Str.from_utf32
Section titled “Str.from_utf32”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)Str.from_utf32_lossy
Section titled “Str.from_utf32_lossy”fn(code_points: list[integer]) -> characterBuilds 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.
Num.to_str
Section titled “Num.to_str”<T: scalar numeric> fn(number: T) -> characterThe number as text, with up to 15 significant digits.
Num.to_str(1 / 3)#> "0.333333333333333"Num.compare
Section titled “Num.compare”<T: scalar numeric> fn(first: T, second: T) -> :LT | :EQ | :GT:LT, :EQ or :GT, comparing first with second.
Num.compare(2L, 1L)#> :GTNum.min
Section titled “Num.min”<T: scalar numeric> fn(first: T, second: T) -> TThe smaller number.
Num.min(2L, 1L)#> 1LNum.max
Section titled “Num.max”<T: scalar numeric> fn(first: T, second: T) -> TThe larger number.
Num.max(2.5, 1)#> 2.5Num.is_lt
Section titled “Num.is_lt”<T: scalar numeric> fn(first: T, second: T) -> logicalWhether first < second.
Num.is_lt(1L, 2L)#> TRUENum.is_gt
Section titled “Num.is_gt”<T: scalar numeric> fn(first: T, second: T) -> logicalWhether first > second.
Num.is_gt(1L, 2L)#> FALSENum.is_lte
Section titled “Num.is_lte”<T: scalar numeric> fn(first: T, second: T) -> logicalWhether first <= second.
Num.is_lte(2, 2L)#> TRUENum.is_gte
Section titled “Num.is_gte”<T: scalar numeric> fn(first: T, second: T) -> logicalWhether first >= second.
Num.is_gte(1.5, 2)#> FALSENum.is_approx_eq
Section titled “Num.is_approx_eq”<T: scalar numeric> fn(first: T, second: T, [rtol]: double, [atol]: double) -> logicalWhether 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)#> TRUENum.is_zero
Section titled “Num.is_zero”<T: scalar numeric> fn(number: T) -> logicalWhether the number is zero.
Num.is_zero(-0)#> TRUENum.is_positive
Section titled “Num.is_positive”<T: scalar numeric> fn(number: T) -> logicalWhether the number is greater than zero.
Num.is_positive(0L)#> FALSENum.is_negative
Section titled “Num.is_negative”<T: scalar numeric> fn(number: T) -> logicalWhether the number is less than zero.
Num.is_negative(-1)#> TRUENum.is_even
Section titled “Num.is_even”<T: scalar numeric> fn(number: T) -> logicalWhether the number is a whole, even number.
Num.is_even(4L)#> TRUENum.is_odd
Section titled “Num.is_odd”<T: scalar numeric> fn(number: T) -> logicalWhether the number is a whole, odd number.
Num.is_odd(-3L)#> TRUENum.is_multiple_of
Section titled “Num.is_multiple_of”<T: scalar numeric> fn(first: T, second: T) -> logicalWhether first is a whole multiple of second. Only zero is a multiple of zero.
Num.is_multiple_of(12L, 4L)#> TRUENum.is_finite
Section titled “Num.is_finite”<T: scalar numeric> fn(number: T) -> logicalWhether the number is neither infinite nor NaN.
Num.is_finite(1 / 0)#> FALSENum.is_infinite
Section titled “Num.is_infinite”<T: scalar numeric> fn(number: T) -> logicalWhether the number is Inf or -Inf.
Num.is_infinite(-1 / 0)#> TRUENum.abs
Section titled “Num.abs”<T: scalar numeric> fn(number: T) -> TThe absolute value.
Num.abs(-3L)#> 3LNum.abs_diff
Section titled “Num.abs_diff”<T: scalar numeric> fn(first: T, second: T) -> TThe absolute difference of two numbers. An integer result that overflows is an error.
Num.abs_diff(3L, 10L)#> 7LNum.neg
Section titled “Num.neg”<T: scalar numeric> fn(number: T) -> TThe number with its sign flipped.
Num.neg(2.5)#> -2.5Num.ceiling
Section titled “Num.ceiling”<T: scalar numeric> fn(number: T) -> TRounds up to a whole number, keeping the number type.
Num.ceiling(1.2)#> 2Num.floor
Section titled “Num.floor”<T: scalar numeric> fn(number: T) -> TRounds down to a whole number, keeping the number type.
Num.floor(-1.2)#> -2Num.round
Section titled “Num.round”<T: scalar numeric> fn(number: T) -> TRounds to the nearest whole number, halves away from zero, keeping the number type.
Num.round(2.5)#> 3Num.pow
Section titled “Num.pow”<T: scalar numeric> fn(base: T, exponent: T) -> doublebase 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)#> 1024Num.sin
Section titled “Num.sin”<T: scalar numeric> fn(number: T) -> doubleThe 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)#> 0Num.cos
Section titled “Num.cos”<T: scalar numeric> fn(number: T) -> doubleThe cosine of an angle in radians.
Num.cos(0)#> 1Num.tan
Section titled “Num.tan”<T: scalar numeric> fn(number: T) -> doubleThe tangent of an angle in radians.
Num.tan(0)#> 0Num.atan
Section titled “Num.atan”<T: scalar numeric> fn(number: T) -> doubleThe arctangent, in radians.
Num.atan(1) * 4#> 3.14159265358979Num.add_checked
Section titled “Num.add_checked”<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)Num.sub_checked
Section titled “Num.sub_checked”<T: scalar numeric> fn(first: T, second: T) -> Result<T, :Overflow | :Undefined>The difference, or :Err(:Overflow).
Num.sub_checked(10L, 3L)#> :Ok(7L)Num.mul_checked
Section titled “Num.mul_checked”<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)Num.add_saturated
Section titled “Num.add_saturated”<T: scalar numeric> fn(first: T, second: T) -> TAdds two numbers, clamping an integer result to the largest or smallest integer.
Num.add_saturated(2147483647L, 1L)#> 2147483647LNum.sub_saturated
Section titled “Num.sub_saturated”<T: scalar numeric> fn(first: T, second: T) -> TSubtracts second from first, clamping an integer result.
Num.sub_saturated(-2147483647L, 1L)#> -2147483647LNum.mul_saturated
Section titled “Num.mul_saturated”<T: scalar numeric> fn(first: T, second: T) -> TMultiplies two numbers, clamping an integer result.
Num.mul_saturated(100000L, 100000L)#> 2147483647LNum.div_checked
Section titled “Num.div_checked”<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)Num.div_trunc_checked
Section titled “Num.div_trunc_checked”<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)Num.div_ceil_checked
Section titled “Num.div_ceil_checked”<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)Num.rem_checked
Section titled “Num.rem_checked”<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)Num.sqrt_checked
Section titled “Num.sqrt_checked”<T: scalar numeric> fn(number: T) -> Result<double, :SqrtOfNegative>The square root, or :Err(:SqrtOfNegative).
Num.sqrt_checked(-1)#> :Err(:SqrtOfNegative)Num.log_checked
Section titled “Num.log_checked”<T: scalar numeric> fn(number: T) -> Result<double, :LogNeedsPositive>The natural logarithm, or :Err(:LogNeedsPositive).
Num.log_checked(exp(2))#> :Ok(2)Num.to_double
Section titled “Num.to_double”<T: scalar numeric> fn(number: T) -> doubleThe number as a double.
Num.to_double(3L)#> 3Num.to_integer_checked
Section titled “Num.to_integer_checked”<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)Num.to_integer
Section titled “Num.to_integer”<T: scalar numeric> fn(number: T) -> integerThe 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)#> -3LNum.add
Section titled “Num.add”<T: scalar numeric> fn(first: T, second: T) -> TThe 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)#> 6LNum.sub
Section titled “Num.sub”<T: scalar numeric> fn(first: T, second: T) -> Tsecond 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)#> 6LNum.mul
Section titled “Num.mul”<T: scalar numeric> fn(first: T, second: T) -> TThe product. An integer product that overflows is an error.
Num.mul(6L, 7L)#> 42LNum.div
Section titled “Num.div”<T: scalar numeric> fn(first: T, second: T) -> doubleThe quotient as a double. A zero divisor is an error; Num.div_checked returns :Err(:DivByZero) instead.
Num.div(7L, 2L)#> 3.5Num.div_trunc
Section titled “Num.div_trunc”<T: scalar numeric> fn(first: T, second: T) -> TThe quotient rounded toward zero. A zero divisor is an error.
Num.div_trunc(-7L, 2L)#> -3LNum.div_ceil
Section titled “Num.div_ceil”<T: scalar numeric> fn(first: T, second: T) -> TThe quotient rounded up. A zero divisor is an error.
Num.div_ceil(7L, 2L)#> 4LNum.rem
Section titled “Num.rem”<T: scalar numeric> fn(first: T, second: T) -> TThe remainder of the truncated division, with the sign of first. A zero divisor is an error.
Num.rem(-7L, 2L)#> -1LNum.pow_int
Section titled “Num.pow_int”fn(base: integer, exponent: integer) -> integerbase 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)#> 81LNum.sqrt
Section titled “Num.sqrt”<T: scalar numeric> fn(number: T) -> doubleThe square root. A negative number is an error; Num.sqrt_checked returns :Err(:SqrtOfNegative) instead.
Num.sqrt(16L)#> 4Num.log
Section titled “Num.log”<T: scalar numeric> fn(number: T) -> doubleThe natural logarithm. A number that is not positive is an error; Num.log_checked returns :Err(:LogNeedsPositive) instead.
Num.log(Num.e)#> 1Num.asin
Section titled “Num.asin”<T: scalar numeric> fn(number: T) -> doubleThe arcsine in radians. A number outside -1 to 1 is an error.
Num.asin(1)#> 1.5707963267949Num.acos
Section titled “Num.acos”<T: scalar numeric> fn(number: T) -> doubleThe arccosine in radians. A number outside -1 to 1 is an error.
Num.acos(1)#> 0Num.is_nan
Section titled “Num.is_nan”<T: scalar numeric> fn(number: T) -> logicalWhether the number is NaN. It is the one library function that accepts NaN; NA is still an error.
Num.is_nan(0 / 0)#> TRUENum.bitwise_and
Section titled “Num.bitwise_and”fn(first: integer, second: integer) -> integerThe bits set in both integers, in 32-bit two’s complement.
Num.bitwise_and(12L, 10L)#> 8LNum.bitwise_or
Section titled “Num.bitwise_or”fn(first: integer, second: integer) -> integerThe bits set in either integer.
Num.bitwise_or(12L, 10L)#> 14LNum.bitwise_xor
Section titled “Num.bitwise_xor”fn(first: integer, second: integer) -> integerThe 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)#> 6LNum.bitwise_not
Section titled “Num.bitwise_not”fn(number: integer) -> integerThe integer with every bit flipped, which is -number - 1.
Num.bitwise_not(5L)#> -6LNum.shift_left_by
Section titled “Num.shift_left_by”fn(number: integer, bits: integer) -> integerShifts 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)#> 16LNum.shift_right_by
Section titled “Num.shift_right_by”fn(number: integer, bits: integer) -> integerShifts the bits right by bits, copying the sign bit in, so a negative number stays negative.
Num.shift_right_by(-16L, 2L)#> -4LNum.shift_right_zf_by
Section titled “Num.shift_right_zf_by”fn(number: integer, bits: integer) -> integerShifts the bits right by bits, filling with zeros.
Num.shift_right_zf_by(-16L, 28L)#> 15LNum.count_one_bits
Section titled “Num.count_one_bits”fn(number: integer) -> integerThe number of set bits.
Num.count_one_bits(255L)#> 8LNum.count_leading_zero_bits
Section titled “Num.count_leading_zero_bits”fn(number: integer) -> integerThe number of zero bits above the highest set bit, 32 for zero.
Num.count_leading_zero_bits(1L)#> 31LNum.count_trailing_zero_bits
Section titled “Num.count_trailing_zero_bits”fn(number: integer) -> integerThe number of zero bits below the lowest set bit, 32 for zero.
Num.count_trailing_zero_bits(8L)#> 3LNum.pi
Section titled “Num.pi”doubleThe ratio of a circle’s circumference to its diameter. It is a value, not a function.
Num.pi#> 3.14159265358979Num.tau
Section titled “Num.tau”doubleThe ratio of a circle’s circumference to its radius, 2 * Num.pi.
Num.tau#> 6.28318530717959doubleEuler’s number, the base of the natural logarithm.
Num.e#> 2.71828182845905Num.max_integer
Section titled “Num.max_integer”integerThe largest integer, 2147483647L. It is a value, not a function.
Num.max_integer#> 2147483647LNum.min_integer
Section titled “Num.min_integer”integerThe smallest integer, -2147483647L. R stores NA in the one value below it, so that value is not
an integer here.
Num.min_integer#> -2147483647LNum.max_double
Section titled “Num.max_double”doubleThe largest finite double.
Num.max_double#> 1.79769313486232e+308Num.min_double
Section titled “Num.min_double”doubleThe most negative finite double, the negation of Num.max_double.
Num.min_double#> -1.79769313486232e+308Num.infinity
Section titled “Num.infinity”doublePositive 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.
Bool.true
Section titled “Bool.true”logicalTRUE, as Roc spells it. It is a value, not a function.
Bool.true#> TRUEBool.false
Section titled “Bool.false”logicalFALSE.
Bool.false#> FALSEBool.and
Section titled “Bool.and”fn(first: logical, second: logical) -> logicalWhether both are TRUE. Both arguments are evaluated, unlike with &&.
Bool.and(TRUE, FALSE)#> FALSEBool.or
Section titled “Bool.or”fn(first: logical, second: logical) -> logicalWhether either is TRUE. Both arguments are evaluated, unlike with ||.
Bool.or(TRUE, FALSE)#> TRUEBool.not
Section titled “Bool.not”fn(value: logical) -> logicalThe opposite value.
List.keep_if(list(1L, 2L, 3L), function(x) Bool.not(Num.is_even(x)))#> list(1L, 3L)Bool.is_eq
Section titled “Bool.is_eq”<T> fn(first: T, second: T) -> logicalWhether 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)))#> TRUEBool.is_not_eq
Section titled “Bool.is_not_eq”<T> fn(first: T, second: T) -> logicalWhether two values differ by identical().
Bool.is_not_eq(list(1L), list(2L))#> TRUEA dictionary maps keys to values, typed Dict<K, V>. See Dictionaries and sets for the rules on keys.
Dict.empty
Section titled “Dict.empty”<K: key, V> fn() -> Dict<K, V>A dictionary with no entries.
Dict.empty()#> Dict.empty()Dict.with_capacity
Section titled “Dict.with_capacity”<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))#> 64LDict.single
Section titled “Dict.single”<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)))Dict.from_list
Section titled “Dict.from_list”<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)))Dict.to_list
Section titled “Dict.to_list”<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))Dict.len
Section titled “Dict.len”<K: key, V> fn(dict: Dict<K, V>) -> integerThe number of entries.
Dict.len(Dict.single("a", 1))#> 1LDict.is_empty
Section titled “Dict.is_empty”<K: key, V> fn(dict: Dict<K, V>) -> logicalWhether the dictionary has no entries.
Dict.is_empty(Dict.empty())#> TRUEDict.get
Section titled “Dict.get”<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)Dict.contains
Section titled “Dict.contains”<K: key, V> fn(dict: Dict<K, V>, key: K) -> logicalWhether the dictionary has an entry for key.
Dict.contains(Dict.single("a", 1), "b")#> FALSEDict.insert
Section titled “Dict.insert”<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)))Dict.remove
Section titled “Dict.remove”<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)))Dict.update
Section titled “Dict.update”<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)))Dict.keys
Section titled “Dict.keys”<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")Dict.values
Section titled “Dict.values”<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)Dict.walk
Section titled “Dict.walk”<K: key, V, S> fn(dict: Dict<K, V>, state: S, f: fn(S, K, V) -> S) -> SFolds 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)#> 3Dict.walk_until
Section titled “Dict.walk_until”<K: key, V, S> fn(dict: Dict<K, V>, state: S, f: fn(S, K, V) -> :Continue(S) | :Break(S)) -> SFolds 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))#> 1LDict.map
Section titled “Dict.map”<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)))Dict.join_map
Section titled “Dict.join_map”<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")))Dict.keep_if
Section titled “Dict.keep_if”<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)))Dict.drop_if
Section titled “Dict.drop_if”<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)))Dict.insert_all
Section titled “Dict.insert_all”<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)))Dict.keep_shared
Section titled “Dict.keep_shared”<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)))Dict.remove_all
Section titled “Dict.remove_all”<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)))Dict.reserve
Section titled “Dict.reserve”<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)))Dict.capacity
Section titled “Dict.capacity”<K: key, V> fn(dict: Dict<K, V>) -> integerHow many entries the dictionary holds before its storage must grow.
Dict.capacity(Dict.reserve(Dict.single("a", 1L), 9L))#> 10LDict.release_excess_capacity
Section titled “Dict.release_excess_capacity”<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)))#> 1LDict.clear
Section titled “Dict.clear”<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>.
Set.empty
Section titled “Set.empty”<T: key> fn() -> Set<T>A set with no elements.
Set.empty()#> Set.empty()Set.with_capacity
Section titled “Set.with_capacity”<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"))#> 64LSet.single
Section titled “Set.single”<T: key> fn(element: T) -> Set<T>A set with one element.
Set.single("a")#> Set.from_list(list("a"))Set.from_list
Section titled “Set.from_list”<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))Set.to_list
Section titled “Set.to_list”<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")Set.len
Section titled “Set.len”<T: key> fn(set: Set<T>) -> integerThe number of elements.
Set.len(Set.from_list(list(1L, 1L, 2L)))#> 2LSet.is_empty
Section titled “Set.is_empty”<T: key> fn(set: Set<T>) -> logicalWhether the set has no elements.
Set.is_empty(Set.empty())#> TRUESet.contains
Section titled “Set.contains”<T: key> fn(set: Set<T>, element: T) -> logicalWhether element is in the set.
Set.contains(Set.from_list(list(1L, 2L)), 2L)#> TRUESet.insert
Section titled “Set.insert”<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))Set.remove
Section titled “Set.remove”<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))Set.union
Section titled “Set.union”<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))Set.intersection
Section titled “Set.intersection”<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))Set.difference
Section titled “Set.difference”<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))Set.walk
Section titled “Set.walk”<T: key, S> fn(set: Set<T>, state: S, f: fn(S, T) -> S) -> SFolds over the elements.
Set.walk(Set.from_list(list(1L, 2L, 3L)), 0L, function(total, x) total + x)#> 6LSet.walk_until
Section titled “Set.walk_until”<T: key, S> fn(set: Set<T>, state: S, f: fn(S, T) -> :Continue(S) | :Break(S)) -> SFolds 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))#> 3LSet.map
Section titled “Set.map”<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))Set.join_map
Section titled “Set.join_map”<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))Set.keep_if
Section titled “Set.keep_if”<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))Set.drop_if
Section titled “Set.drop_if”<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))Set.reserve
Section titled “Set.reserve”<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"))Set.capacity
Section titled “Set.capacity”<T: key> fn(set: Set<T>) -> integerHow many elements the set holds before its storage must grow.
Set.capacity(Set.reserve(Set.single("a"), 9L))#> 10LSet.release_excess_capacity
Section titled “Set.release_excess_capacity”<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