Skip to content

Builtin Functions

The language trys to follow Python's batteries included motto, where-by all desired functionality is included out of the box. This is achieved by the inclusion of many different builtin functions, allowing you tackle many general-purpose and Advent of Code specific problems. The following builtin functions are available in all runtimes:

Collection

list

list(value)

Return the List representation of the given value.

list([1, 2, 3])
list({1, 2, 3})

Ouput is a List of List tuples [key, value].

list(#{1: 2, 3: 4})
list("ab")
list(1..5)
list(1..=5)

set

set(value)

Return the Set representation of the given value.

set([1, 2, 3])
set({1, 2, 3})
set("ab")
set(1..5)
set(1..=5)

dict

dict(value)

Return the Dictionary representation of the given value.

Input is a List of List tuples [key, value].

dict([[1, 2], [3, 4]])
dict(#{1: 2, 3: 4})

get

get(index, collection)

Get an element within a collection following the indexing rules. If an element can not be found at that index then nil is returned.

get(1, [1, 2])
get(1, {1, 2})
get(1, #{1: 2, 3: 4})
get(1, "ab")
get(1, 1..5)
get(1, 1..=5)
get(1, 0..)

size

size(collection)

Get the size of a collection.

size([1, 2])
size({1, 2})
size(#{1: 2, 3: 4})
size("ab")
size(1..5)
size(1..=5)

push

push(value, collection)

Add a new value to a collection.

The value is appended to the end of the List.

push(3, [1, 2])
push(3, {1, 2})

assoc

assoc(key, value, collection)

Associate the provided key/index with the given value in a collection.

assoc(0, 3, [1, 2])

If the index is not already present nil values are inserted up to the given index.

assoc(1, 1, [])
assoc(1, 1, #{1: 2, 3: 4})
assoc(0, 1, #{1: 2, 3: 4})

update

update(key, updater, collection)

Update the given index/key of a collection using the supplied pure updater function. The updater function is supplied the current value at the given index/key, if not present nil is supplied.

update(0, _ + 1, [1, 2])

If the index is not already present nil values are inserted up to the given index.

update(1, || 1, [])
update(0, || 1, #{})
update(1, _ + 1, #{1: 2, 3: 4})

update_d

update_d(key, default, updater, collection)

Update the given index/key of a collection using the supplied pure updater function. The updater function is supplied the current value at the given index/key, if not present the default value is supplied.

update_d(0, 0, _ + 1, [1, 2])

If the index is not already present nil values are inserted up to the given index.

update_d(1, 0, _ + 1, [])
update_d(0, 0, _ + 1, #{})
update_d(1, 0, _ + 1, #{1: 2, 3: 4})

map

map(mapper, collection)

Return a collection with a pure mapper function applied over each element within the given collection.

map(_ + 1, [1, 2])
map(_ + 1, {1, 2})
map(_ + 1, #{1: 2, 3: 4})

The mapper function is suppled both the value and key in the context of a Dictionary.

map(|_, k| k + 1, #{1: 2, 3: 4})

Each character is considered an element within the mapping. The returned collection is a List.

map(_ * 2, "ab")

Lazy Sequences return another Lazy Sequence, which when resolved will lazily apply the required mapping.

map(_ + 1, 1..5) |> list
map(_ + 1, 1..=5) |> list
map(_ + 1, 0..) |> take(3)
map(_ + 1, repeat(1)) |> take(3)

filter

filter(predicate, collection)

Return a collection based on a pure predicate function holding truthy for the given element in a collection.

filter(_ == 1, [1, 2])
filter(_ == 1, {1, 2})
filter(_ == 2, #{1: 2, 3: 4})

The predicate function is suppled both the value and key in the context of a Dictionary.

filter(|_, k| k == 3, #{1: 2, 3: 4})

Each character is considered an element within the predicate. The returned collection is a List.

filter(_ == "a", "ab")

Lazy Sequences return another Lazy Sequence, which when resolved will lazily apply the required filter.

filter(_ % 2, 1..5) |> list
filter(_ % 2, 1..=5) |> list
filter(_ % 2, 0..) |> take(3)
filter(_ != 2, cycle([1, 2, 3])) |> take(3)

each

each(side_effect, collection)

Apply a side-effecting function over each element in the given collection.

let mut acc = 0;
each(|v| acc = acc + v, [1, 2]);
acc;
let mut acc = 0;
each(|v| acc = acc + v, {1, 2});
acc;
let mut acc = 0;
each(|v| acc = acc + v, #{1: 2, 3: 4});
acc;

The predicate function is suppled both the value and key in the context of a Dictionary.

let mut acc = 0;
each(|_, k| acc = ac + k, #{1: 2, 3: 4});
acc;

Each character is considered an element within the iteration. The returned collection is a List.

let mut acc = 0;
each(|_| acc = acc + 1, "ab");
acc;

The function can break which will terminate the collection iteration early.

let mut acc = 0;
each(|v| acc = acc + v, 1..5);
acc;
let mut acc = 0;
each(|v| acc = acc + v, 1..=5);
acc;
let mut acc = 0;
0.. |> each |v| {
  if v == 10 { break nil } else { acc = acc + v }
};
acc;
let mut acc = 0;
iterate(_ + 1, 1) |> each |v| {
  if v == 10 { break nil } else { acc = acc + v }
};
acc;

reduce

reduce(reducer, collection)

Apply a pure reducer function over a given collection. The initial accumulator value supplied upon first iteration is the first element in the collection. If the collection is empty then an error is thrown.

reduce(+, [1, 2])
reduce(+, {1, 2})
reduce(+, #{1: 2, 3: 4})

The reducer function is suppled both the value and key in the context of a Dictionary.

reduce(|acc, _, k| acc + k, #{1: 2, 3: 4})

Each character is considered an element within the reduction. The returned collection is a List.

reduce(|acc, ch| ch + acc, "ab")

The function can break which will terminate the collection iteration early.

reduce(+, 1..5)
reduce(+, 1..=5)
0.. |> reduce |acc, v| {
  if v == 10 { break acc } else { acc + v }
}
iterate(_ + 1, 1) |> reduce |acc, v| {
  if v == 10 { break acc } else { acc + v }
}

fold

fold(initial, folder, collection)

Apply a pure folder function over a given collection. The initial fold receives the first element and the initial value supplied. If the collection is empty then the initial value is returned.

fold(0, +, [1, 2])
fold(0, +, {1, 2})
fold(0, +, #{1: 2, 3: 4})

The folder function is suppled both the value and key in the context of a Dictionary.

fold(0, |acc, _, k| acc + k, #{1: 2, 3: 4})

Each character is considered an element within the fold. The returned collection is a List.

fold(0, _ + 1, "ab")

The function can break which will terminate the collection iteration early.

fold(0, +, 1..5)
fold(0, +, 1..=5)
0.. |> fold (0) |acc, v| {
  if v == 10 { break acc } else { acc + v }
}
iterate(_ + 1, 1) |> fold (0) |acc, v| {
  if v == 10 { break acc } else { acc + v }
}

fold_s

fold_s(initial, folder, collection)

Apply a pure folder function over a given collection, with optional state which is passed along throughout the fold. The accumulated value is a List comprising of the first element being the resulting folded value, and other elements being state you wish to pass on to the next iteration. Upon completion, the extra state is discarded and the folded value is returned. If the collection is empty then the initial value is returned.

50..100 |> fold_s(
  [0, 0, 0],
  |[acc, x, y], val| [acc + x * y * val, val, val / 2]
)

find

find(predicate, collection)

Apply a pure predicate function over a given collection, returning the first element where the predicate holds truthy.

find(_ % 2, [1, 2])
find(_ % 2, {1, 2})
find(_ % 2, #{1: 2, 3: 4})

The predicate function is suppled both the value and key in the context of a Dictionary.

find(|_, k| k % 2, #{1: 2, 3: 4})

Each character is considered an element within the predicate. The returned collection is a List.

find(_ == "b", "ab")
find(_ % 2, 1..5)
find(_ % 2, 1..=5)
find(_ % 2, 0..)
find(_ % 2, iterate(_ + 1, 1))

scan

scan(initial, folder, collection)

Return a collection which includes the result of each iteration of folding a pure folder function over each element within the given collection.

scan(0, +, [1, 2])
scan(0, +, {1, 2})
scan(0, +, #{1: 2, 3: 4})

The folder function is suppled both the value and key in the context of a Dictionary.

scan(0, |acc, _, k| acc + k, #{1: 2, 3: 4})

Each character is considered an element within the fold. The returned collection is a List.

scan("", +, "ab")
scan(0, +, 1..5)
scan(0, +, 1..=5)

flat_map

flat_map(mapper, collection)

Apply a pure mapper function over a given collection with the resulting mapped List results being flattened into a single List.

flat_map(_ * 2, [[1, 2], [3, 4]])

filter_map

filter_map(mapper, collection)

Apply a pure mapper function over a given collection and filter out the mapped values based on them being truthy. This is a convenience function (inspired by Rust) for the common place map(..) |> filter(..) pattern.

[1, 2, 3, 4] |> filter_map(|v| if v % 2 { v * 2 })
{1, 2, 3, 4} |> filter_map(|v| if v % 2 { v * 2 })
#{1: 2, 3: 4} |> filter_map(|v| if v != 2 { v * 2 })

The mapper function is suppled both the value and key in the context of a Dictionary.

#{1: 2, 3: 4} |> filter_map(|_, k| if k != 1 { k * 2 })

Each character is considered an element within the mapping. The returned collection is a List.

"ab" |> filter_map(|v| if v != "a" { v * 2 })
1..5
  |> filter_map(|v| if v % 2 { v * 2 })
  |> list
1..=5
  |> filter_map(|v| if v % 2 { v * 2 })
  |> list
1..
  |> filter_map(|v| if v % 2 { v * 2 })
  |> take(3)
iterate(_ + 1, 1)
  |> filter_map(|v| if v % 2 { v * 2 })
  |> take(3)

find_map

find_map(mapper, collection)

Apply a pure mapper function over a given collection and find the first mapped element where the value returned is truthy. This is a convenience function (inspired by Rust) for the common place map(..) |> find(..) pattern.

[1, 2] |> find_map(|v| if v % 2 { v * 2 })
{1, 2} |> find_map(|v| if v % 2 { v * 2 })
#{1: 2, 3: 4} |> find_map(|v| if v != 2 { v * 2 })

The mapper function is suppled both the value and key in the context of a Dictionary.

#{1: 2, 3: 4} |> find_map(|_, k| if k != 1 { k * 2 })

Each character is considered an element within the mapping. The returned collection is a List.

"ab" |> find_map(|v| if v != "a" { v * 2 })
1..5 |> find_map(|v| if v % 2 { v * 2 })
1..=5 |> find_map(|v| if v % 2 { v * 2 })
1.. |> find_map(|v| if v % 2 { v * 2 })
iterate(_ + 1, 1)
  |> find_map(|v| if v % 2 { v * 2 })

count

count(predicate, collection)

Count the total number of elements where the pure predicate function holds truthy.

count(_ % 2, [1, 2, 3, 4])
count(_ % 2, {1, 2, 3, 4})
count(_ % 2, #{1: 2, 3: 4})

The predicate function is suppled both the value and key in the context of a Dictionary.

count(|_, k| k % 2, #{1: 2, 3: 4})

Each character is considered an element within the predicate. The returned collection is a List.

count(_ == "a", "ab")
count(_ % 2, 1..5)
count(_ % 2, 1..=5)

zip

zip(collection, ..collections)

Takes any number of iterables as an argument and aggregates them together producing a List/Lazy Sequence of List tuples. Each List tuple contains elements of all iterables occurring at the same position, stopping when the shortest iterables is exhausted.

zip(0.., "abc", [1.5, 2.5, 3.5])
zip(0.., "abcdef", [1.5, 2.5, 3.5])

If any of the iterables have a finite size then a List is returned, else a Lazy Sequence is produced.

zip(0.., 1..) |> take(3)

sum

sum(collection)

Sum all the Integer elements within a collection.

sum([1, 2])
sum({1, 2})
sum(#{1: 2, 3: 4})
sum(1..5)
sum(1..=5)

max

max(..values)

Find the largest (maximum) element within a collection. The collections can be supplied as a single argument List (containing multiple collections), or as a multi-arity function call.

max(1, 2) == max([1, 2])
max([1, 2])
max({1, 2})
max(#{1: 2, 3: 4})
max(1..5)
max(1..=5)

min

min(..values)

Find the smallest (minimum) element within a collection. The collections can be supplied as a single argument List (containing multiple collections), or as a multi-arity function call.

min(1, 2) == min([1, 2])
min([1, 2])
min({1, 2})
min(#{1: 2, 3: 4})
min(1..5)
min(1..=5)

skip

skip(total, collection)

Skip a number of elements within a collection. If the collection is a Lazy Sequence the skip is applied when the collection is lazily resolved.

skip(1, [1, 2, 3])
skip(1, {1, 2, 3})
skip(2, 1..5)
skip(2, 1..=5)
skip(2, 1..) |> take(3)
skip(2, iterate(_ + 1, 1)) |> take(3)

take

take(total, collection)

Take a number of elements from a collection. If the collection is a Lazy Sequence then the collection is resolved with any outstanding operations (map, skip etc.) being applied.

take(2, [1, 2, 3])
take(2, {1, 2, 3})
take(2, 1..5)
take(2, 1..=5)
take(2, 1..)
take(2, iterate(_ + 1, 1))

sort

sort(comparator, collection)

Sort the collection based on a supplied pure comparator function. The comparator function accepts two values (a, b) from the collection and can either return:

An Boolean value, with false signifying a < b and true signifying a > b.

sort(>, [3, 2, 1])

An Integer value, with a negative value signifying a < b, zero signifying a == b, and a positive value signifying a > b.

sort(-, [3, 2, 1])

reverse

reverse(collection)

Reverse the order of a given List collection.

reverse([1, 2, 3])
reverse("abc")
reverse(1..5)
reverse(1..=5)

repeat

repeat(value)

Generate a Lazy Sequence which repeats the provided value indefinitely.

repeat(1) |> take(3)

cycle

cycle(list)

Generate a Lazy Sequence which cycles through each element in a List indefinitely, looping back to the start once exhausted.

cycle([1, 2, 3]) |> take(4)

Each character is considered an element within the Lazy Sequence.

cycle("abc") |> take(4)

iterate

iterate(generator, initial)

Generate a Lazy Sequence which supplies a provided pure generator function with the previous result (starting with an initial value) to produce the next value in the sequence.

iterate(|[a, b]| [b, a + b], [0, 1])
  |> skip(9)
  |> take(1)
iterate(_ * 2, 1) |> take(5)

keys

keys(dictionary)

Return the keys in a given Dictionary as a List.

keys(#{1: 2, 3: 4})

values

values(dictionary)

Return the values in a given Dictionary as a List.

values(#{1: 2, 3: 4})

first

first(collection)

Return the first element within the collection (aka head). If the collection is empty then nil is returned.

first([1, 2])
first({1, 2})

Each character is considered an element.

first("ab")
first(1..5)
first(1..=5)
first(1..)
first(iterate(_ + 1, 1))

second

second(collection)

Return the second element within the collection. If the collection does not contain a second element then nil is returned.

second([1, 2])
second({1, 2})

Each character is considered an element.

second("ab")
second(1..5)
second(1..=5)
second(1..)
second(iterate(_ + 1, 1))

rest

rest(collection)

Return the collection with the first element omitted (aka tail). If the collection does not have more than one element then an empty List is returned.

rest([1, 2])
rest({1, 2})

Each character is considered an element.

rest("ab")
rest(1..5)
rest(1..=5)
rest(1..)
rest(iterate(_ + 1, 1))

union

union(..values)

Return the elements (as a Set) which are found in any of the provided collections. The collections can be supplied as a single argument List (containing multiple collections), or as a multi-arity function call.

union([{1, 2}, [2, 3], 1..4, "abc"])
union({1, 2}, [2, 3], 1..4, "abc")

intersection

intersection(..values)

Return the elements (as a Set) which are found in all the provided collections. The collections can be supplied as a single argument List (containing multiple collections), or as a multi-arity function call.

intersection([{1, 2}, [2, 3], 1..4])
intersection({1, 2}, [2, 3], 1..4)

rotate

rotate(steps, collection)

Rotate a given List a number of steps. If the step number is positive the rotation proceed forward, with the last item moving to the start of the List. If the step number is negative the rotation will go backwards, with the first item moving to the end of the List.

rotate(1, [1, 2, 3])
rotate(-1, [1, 2, 3])

chunk

chunk(size, collection)

Split a List into chunks based on a given size. If the List size is not divisible by the chunk size then the last chunk will contain fewer than the desired elements.

chunk(2, [1, 2, 3])
chunk(2, [1, 2, 3, 4])

combinations

combinations(size, collection)

Generate a Lazy Sequence which produces all the possible combinations of a desired number of elements from within a List.

combinations(2, [1, 2, 3, 4, 5]) |> list
combinations(3, [1, 2, 3, 4, 5]) |> find(|x| sum(x) == 10)

includes?

includes?(collection, value)

Predicate to assert if a value is present within a given collection, based on equality rules.

includes?([1, 2], 1)
includes?({1, 2}, 1)

Each character is considered an element.

includes?("ab", "a")
includes?(1..5, 1)
includes?(1..=5, 1)
includes?(1.., 5)
includes?(iterate(_ + 1, 1), 5)

excludes?

excludes?(collection, value)

Predicate to assert if a value is not present within a given collection, based on equality rules.

excludes?([1, 2], 3)
excludes?({1, 2}, 3)

Each character is considered an element.

excludes?("ab", "c")
excludes?(1..5, 6)
excludes?(1..=5, 6)

any?

any?(predicate, collection)

Predicate to assert if any value within the collection holds truthy based on the supplied pure predicate function.

any?(_ == 1, [1, 2])
any?(_ == 1, {1, 2})

Each character is considered an element.

any?(_ == "a", "ab")
any?(_ == 1, 1..5)
any?(_ == 1, 1..=5)
any?(_ == 1, 1..)
any?(_ == 1, iterate(_ + 1, 1))

all?

all?(predicate, collection)

Predicate to assert if all values within the collection hold truthy based on the supplied pure predicate function.

all?(_ > 0, [1, 2])
all?(_ > 0, {1, 2})

Each character is considered an element.

all?(_ != "c", "ab")
all?(_ > 0, 1..5)
all?(_ > 0, 1..=5)

Math

abs

abs(value)

Return the absolute value of a number.

abs(-1)
abs(-1.5)

vec_add

vec_add(a, b)

Sum two Lists together using Vector addition. The resulting List will contain results up to the shortest List's size.

vec_add([1, 2], [3, 4])
vec_add([1, 2, 3], [4, 5, 6])

signum

signum(value)

Return the sign (-1, 0, 1) for the given number.

signum(5)
signum(-5.5)

Bitwise

bit_and

bit_and(a, b)

Return an Integer whose binary representation has a 1 in each bit position for which the corresponding bits of both operands are 1.

bit_and(9, 11) // 1001 & 1011

bit_or

bit_or(a, b)

Return an Integer whose binary representation has a 1 in each bit position for which the corresponding bits of either or both operands are 1.

bit_or(9, 11) // 1001 | 1011

bit_xor

bit_xor(a, b)

Return an Integer whose binary representation has a 1 in each bit position for which the corresponding bits of either but not both operands are 1.

bit_xor(9, 11) // 1001 ^ 1011

bit_shift_left

bit_shift_left(value, shift)

Return an Integer whose binary representation is the first operand shifted by the specified number of bits to the left.

bit_shift_left(1, 3)

bit_shift_right

bit_shift_right(value, shift)

Return an Integer whose binary representation is the first operand shifted by the specified number of bits to the right.

bit_shift_right(64, 3)

String

int

int(value)

Attempt to parse the provided value into an Integer representation.

int(5)
int(-5.5)
int("5")
int(true)

Upon failure to parse the value the Integer 0 is returned.

int("invalid")

ints

ints(value)

Return all parseable Integer values (as per int) from a String value as a List. If no Integers are found and empty List is returned.

ints("1,2,3")
ints("15a20b35")

lines

lines(value)

Split a given String into a List of Strings, seperated on new lines \n.

lines("a\nb\nc")

split

split(seperator, value)

Split a given String into a List of Strings, seperated based on the provided value.

split("-", "a-b-c")

regex_match

regex_match(pattern, value)

Match and capture values from a subject String based on a provided Regular Expression. Captured values are returned as a List of Strings. If no match/capture can be found an empty List is returned.

regex_match("name: (\\w+), age: (\\d+)", "name: Bob, age: 30")

regex_match_all

regex_match_all(pattern, value)

Match and capture all occurrences from a subject String based on a provided Regular Expression. Captured values are returned as a List of Strings. If no match/capture can be found an empty List is returned.

regex_match_all("\\w+: \\w+", "name: Bob, age: 30")

Miscellaneous

range

range(from, to, step)

Generate an Inclusive Range using a custom step value (not the default +1, -1).

range(1, 10, 2) |> list

id

id(value)

Return the value passed in as an argument.

id(5)

memoize

memoize(function)

Return a function which wraps a given pure function memoizing invocation calls for performance. This is a trade-off between space and time complexity.

let fibonacci = memoize |n| {
  if (n > 1) {
    fibonacci(n - 1) + fibonacci(n - 2)
  } else {
    n
  }
};
fibonacci(30)

evaluate

evaluate(source)

Evaluates the provided String expression within a sandbox santa-lang interpreter.

evaluate("1.. |> filter(_ % 2) |> take(3)")

type

type(value)

Return the type of the given value as a String.

type(1)