list
Functions for working with ordered, immutable lists (List(a)). Lists use
[...] literal syntax and support the range operator 1..5.
See also result / option for the return types of
find, head, last, get, and filter_map; stream for
channel-backed lazy pipelines over the same combinator names.
Summary
| Function | Signature | Description |
|---|---|---|
all | (List(a), Fn(a) -> Bool) -> Bool | True if predicate holds for every element |
any | (List(a), Fn(a) -> Bool) -> Bool | True if predicate holds for at least one element |
append | (List(a), a) -> List(a) | Add an element to the end |
concat | (List(a), List(a)) -> List(a) | Concatenate two lists |
contains | (List(a), a) -> Bool where a: Equal | Check if element is in list |
drop | (List(a), Int) -> List(a) | Remove first n elements |
each | (List(a), Fn(a) -> ()) -> () | Call function for each element (side effects) |
enumerate | (List(a)) -> List((Int, a)) | Pair each element with its index |
filter | (List(a), Fn(a) -> Bool) -> List(a) | Keep elements matching predicate |
filter_map | (List(a), Fn(a) -> Option(b)) -> List(b) | Filter and transform in one pass |
find | (List(a), Fn(a) -> Bool) -> Option(a) | First element matching predicate |
flat_map | (List(a), Fn(a) -> List(b)) -> List(b) | Map then flatten |
flatten | (List(List(a))) -> List(a) | Flatten one level of nesting |
fold | (List(a), b, Fn(b, a) -> b) -> b | Reduce to a single value |
fold_until | (List(a), b, Fn(b, a) -> Step(b)) -> b | Fold with early termination |
get | (List(a), Int) -> Option(a) | Element at index, or None |
group_by | (List(a), Fn(a) -> b) -> Map(b, List(a)) where b: Hash | Group elements by key function |
head | (List(a)) -> Option(a) | First element, or None |
index_of | (List(a), a) -> Option(Int) where a: Equal | Index of first matching element, or None |
intersperse | (List(a), a) -> List(a) | Insert separator between elements |
last | (List(a)) -> Option(a) | Last element, or None |
length | (List(a)) -> Int | Number of elements |
map | (List(a), Fn(a) -> b) -> List(b) | Transform each element |
max_by | (List(a), Fn(a) -> b) -> Option(a) where b: Compare | Element with largest key, or None |
min_by | (List(a), Fn(a) -> b) -> Option(a) where b: Compare | Element with smallest key, or None |
prepend | (List(a), a) -> List(a) | Add an element to the front |
product | (List(Int)) -> Int | Product of a list of ints (1 on empty) |
product_float | (List(Float)) -> Float | Product of a list of floats (1.0 on empty) |
remove_at | (List(a), Int) -> Option(List(a)) | The list without the element at index, or None if there is none |
reverse | (List(a)) -> List(a) | Reverse element order |
scan | (List(a), b, Fn(b, a) -> b) -> List(b) | Prefix fold; returns all intermediate accumulators |
set | (List(a), Int, a) -> Option(List(a)) | The list with the element at index replaced, or None if there is none |
sort | (List(a)) -> List(a) where a: Compare | Sort in natural order |
sort_by | (List(a), Fn(a) -> b) -> List(a) where b: Compare | Sort by key function |
sum | (List(Int)) -> Int | Sum a list of ints (0 on empty) |
sum_float | (List(Float)) -> Float | Sum a list of floats (0.0 on empty) |
tail | (List(a)) -> List(a) | All elements except the first |
take | (List(a), Int) -> List(a) | Keep first n elements |
unfold | (a, Fn(a) -> Option((b, a))) -> List(b) | Build a list from a seed |
unique | (List(a)) -> List(a) where a: Equal | Remove duplicates, preserving first occurrence |
zip | (List(a), List(b)) -> List((a, b)) | Pair elements from two lists |
list.all
list.all(xs: List(a), f: Fn(a) -> Bool) -> Bool
Returns true if f returns true for every element. Short-circuits on the
first false.
import list
fn main() {
let all_even = list.all([2, 4, 6]) { x -> x % 2 == 0 }
println(all_even) -- true
}open in playgroundlist.any
list.any(xs: List(a), f: Fn(a) -> Bool) -> Bool
Returns true if f returns true for at least one element. Short-circuits on
the first true.
import list
fn main() {
let has_even = list.any([1, 3, 4]) { x -> x % 2 == 0 }
println(has_even) -- true
}open in playgroundlist.append
list.append(xs: List(a), elem: a) -> List(a)
Returns a new list with elem added at the end.
import list
fn main() {
let xs = [1, 2, 3] |> list.append(4)
println(xs) -- [1, 2, 3, 4]
}open in playgroundlist.concat
list.concat(xs: List(a), ys: List(a)) -> List(a)
Concatenates two lists into a single list.
import list
fn main() {
let joined = list.concat([1, 2], [3, 4])
println(joined) -- [1, 2, 3, 4]
}open in playgroundlist.contains
list.contains(xs: List(a), elem: a) -> Bool where a: Equal
Returns true if elem is in the list (by value equality).
import list
fn main() {
println(list.contains([1, 2, 3], 2)) -- true
println(list.contains([1, 2, 3], 5)) -- false
}open in playgroundlist.drop
list.drop(xs: List(a), n: Int) -> List(a)
Returns the list without its first n elements. If n >= length, returns an
empty list. Negative n is a runtime error.
import list
fn main() {
let tail = list.drop([1, 2, 3, 4, 5], 2)
println(tail) -- [3, 4, 5]
}open in playgroundlist.each
list.each(xs: List(a), f: Fn(a) -> ()) -> ()
Calls f for every element in the list. Used for side effects. Returns unit.
import list
fn main() {
[1, 2, 3] |> list.each { x -> println(x) }
}open in playgroundlist.enumerate
list.enumerate(xs: List(a)) -> List((Int, a))
Returns a list of (index, element) tuples, with indices starting at 0.
import list
fn main() {
let pairs = list.enumerate(["a", "b", "c"])
-- [(0, "a"), (1, "b"), (2, "c")]
list.each(pairs) { (i, v) -> println("{i}: {v}") }
}open in playgroundlist.filter
list.filter(xs: List(a), f: Fn(a) -> Bool) -> List(a)
Returns a list containing only the elements for which f returns true.
import list
fn main() {
let evens = [1, 2, 3, 4, 5] |> list.filter { x -> x % 2 == 0 }
println(evens) -- [2, 4]
}open in playgroundlist.filter_map
list.filter_map(xs: List(a), f: Fn(a) -> Option(b)) -> List(b)
Applies f to each element. Keeps the inner values from Some results and
discards None results. Combines filtering and mapping in one pass.
import int
import list
fn main() {
let results = ["1", "abc", "3"]
|> list.filter_map { s ->
match int.parse(s) {
Ok(n) -> Some(n * 10)
Err(_) -> None
}
}
println(results) -- [10, 30]
}open in playgroundlist.find
list.find(xs: List(a), f: Fn(a) -> Bool) -> Option(a)
Returns Some(element) for the first element where f returns true, or
None if no match is found.
import list
fn main() {
let first_gt_2 = list.find([1, 2, 3, 4]) { x -> x > 2 }
println(first_gt_2) -- Some(3)
}open in playgroundlist.flat_map
list.flat_map(xs: List(a), f: Fn(a) -> List(b)) -> List(b)
Maps each element to a list, then flattens the results into a single list.
import list
fn main() {
let expanded = [1, 2, 3] |> list.flat_map { x -> [x, x * 10] }
println(expanded) -- [1, 10, 2, 20, 3, 30]
}open in playgroundlist.flatten
list.flatten(xs: List(List(a))) -> List(a)
Flattens one level of nesting. Non-list elements are kept as-is.
import list
fn main() {
let flat = list.flatten([[1, 2], [3], [4, 5]])
println(flat) -- [1, 2, 3, 4, 5]
}open in playgroundlist.fold
list.fold(xs: List(a), init: b, f: Fn(b, a) -> b) -> b
Reduces a list to a single value. Starts with init, then calls f(acc, elem)
for each element.
import list
fn main() {
let sum = [1, 2, 3] |> list.fold(0) { acc, x -> acc + x }
println(sum) -- 6
}open in playgroundlist.fold_until
list.fold_until(xs: List(a), init: b, f: Fn(b, a) -> Step(b)) -> b
Like fold, but the callback returns Continue(acc) to keep going or
Stop(value) to terminate early.
import list
fn main() {
-- Sum until we exceed 5
let partial_sum = list.fold_until([1, 2, 3, 4, 5], 0) { acc, x ->
let next = acc + x
match {
next > 5 -> list.Stop(acc)
_ -> list.Continue(next)
}
}
println(partial_sum) -- 3
}open in playgroundlist.get
list.get(xs: List(a), i: Int) -> Option(a)
Returns Some(element) at the given index, or None if there is no such
position: an index past the end, or a negative one (use list.last for end
access).
import list
fn main() {
let xs = [10, 20, 30]
println(list.get(xs, 1)) -- Some(20)
println(list.get(xs, 10)) -- None
println(list.get(xs, -1)) -- None
}open in playgroundlist.group_by
list.group_by(xs: List(a), f: Fn(a) -> b) -> Map(b, List(a)) where b: Hash
Groups elements by the result of applying f. Returns a map from keys to lists
of elements that produced that key.
import list
fn main() {
let groups = [1, 2, 3, 4, 5, 6] |> list.group_by { x -> x % 2 }
-- #{0: [2, 4, 6], 1: [1, 3, 5]}
}open in playgroundlist.head
list.head(xs: List(a)) -> Option(a)
Returns Some(first_element) or None if the list is empty.
import list
fn main() {
println(list.head([1, 2, 3])) -- Some(1)
println(list.head([])) -- None
}open in playgroundlist.index_of
list.index_of(xs: List(a), target: a) -> Option(Int) where a: Equal
Returns Some(index) of the first element equal to target (by value
equality), or None if no element matches.
import list
fn main() {
println(list.index_of([10, 20, 30, 20], 20)) -- Some(1)
println(list.index_of([10, 20, 30], 99)) -- None
}open in playgroundlist.intersperse
list.intersperse(xs: List(a), sep: a) -> List(a)
Returns a new list with sep inserted between consecutive elements. Empty and
single-element inputs are returned unchanged.
import list
fn main() {
println(list.intersperse([1, 2, 3], 0)) -- [1, 0, 2, 0, 3]
println(list.intersperse([42], 0)) -- [42]
println(list.intersperse([], 0)) -- []
}open in playgroundlist.last
list.last(xs: List(a)) -> Option(a)
Returns Some(last_element) or None if the list is empty.
import list
fn main() {
println(list.last([1, 2, 3])) -- Some(3)
println(list.last([])) -- None
}open in playgroundlist.length
list.length(xs: List(a)) -> Int
Returns the number of elements in the list.
import list
fn main() {
println(list.length([1, 2, 3])) -- 3
println(list.length([])) -- 0
}open in playgroundlist.map
list.map(xs: List(a), f: Fn(a) -> b) -> List(b)
Returns a new list with f applied to each element.
import list
fn main() {
let doubled = [1, 2, 3] |> list.map { x -> x * 2 }
println(doubled) -- [2, 4, 6]
}open in playgroundlist.max_by
list.max_by(xs: List(a), key: Fn(a) -> b) -> Option(a) where b: Compare
Returns Some(element) whose key result is largest, or None if the list
is empty. On ties, returns the first element with the maximum key. Requires
b to support comparison (numbers, strings, etc.).
import list
import string
fn main() {
let words = ["fig", "banana", "apple"]
let longest = list.max_by(words) { w -> string.length(w) }
println(longest) -- Some(banana)
println(list.max_by([]) { x -> x }) -- None
}open in playgroundlist.min_by
list.min_by(xs: List(a), key: Fn(a) -> b) -> Option(a) where b: Compare
Returns Some(element) whose key result is smallest, or None if the list
is empty. On ties, returns the first element with the minimum key. Requires
b to support comparison (numbers, strings, etc.).
import list
import string
fn main() {
let words = ["banana", "fig", "apple"]
let shortest = list.min_by(words) { w -> string.length(w) }
println(shortest) -- Some(fig)
println(list.min_by([]) { x -> x }) -- None
}open in playgroundlist.prepend
list.prepend(xs: List(a), elem: a) -> List(a)
Returns a new list with elem added at the front.
import list
fn main() {
let xs = [2, 3] |> list.prepend(1)
println(xs) -- [1, 2, 3]
}open in playgroundlist.product
list.product(xs: List(Int)) -> Int
Returns the product of all elements. Returns 1 on an empty list. Overflow
is a runtime error; the product of a list that has a 0 in it is 0,
whatever the product of the other elements is.
import list
fn main() {
println(list.product([1, 2, 3, 4])) -- 24
println(list.product([])) -- 1
println(list.product(-100..100)) -- 0
}open in playgroundlist.product_float
list.product_float(xs: List(Float)) -> Float
Like product, but for lists of floats. Returns 1.0 on an empty list.
Raises a runtime error when the product is too large for a Float.
import list
fn main() {
println(list.product_float([1.5, 2.0, 4.0])) -- 12.0
println(list.product_float([])) -- 1.0
}open in playgroundlist.remove_at
list.remove_at(xs: List(a), index: Int) -> Option(List(a))
Returns Some of a new list with the element at index removed, or None if
the list has no such position (an index past the end, or a negative one), as
list.get and list.set do.
import list
fn main() {
println(list.remove_at([10, 20, 30, 40], 1)) -- Some([10, 30, 40])
println(list.remove_at([1, 2], 5)) -- None
}open in playgroundlist.reverse
list.reverse(xs: List(a)) -> List(a)
Returns a new list with elements in reverse order.
import list
fn main() {
println(list.reverse([1, 2, 3])) -- [3, 2, 1]
}open in playgroundlist.set
list.set(xs: List(a), index: Int, value: a) -> Option(List(a))
Returns Some of a new list with the element at index replaced by value,
or None if the list has no such position (an index past the end, or a
negative one).
import list
fn main() {
println(list.set([10, 20, 30], 1, 99)) -- Some([10, 99, 30])
println(list.set([10, 20, 30], 3, 99)) -- None
}open in playgroundlist.sort
list.sort(xs: List(a)) -> List(a) where a: Compare
Returns a new list sorted in natural (ascending) order.
import list
fn main() {
println(list.sort([3, 1, 2])) -- [1, 2, 3]
}open in playgroundlist.scan
list.scan(xs: List(a), init: b, f: Fn(b, a) -> b) -> List(b)
Like fold, but returns every intermediate accumulator rather than just the
final one. The result length is length(xs) + 1: the first element is init,
and each subsequent element is the accumulator after applying f to the next
input. This matches Haskell’s scanl and Rust’s std::iter::successors
(in spirit) — the convention is inclusive of the initial value.
import list
fn main() {
-- Prefix sums: [0, 1, 3, 6, 10]
let sums = list.scan([1, 2, 3, 4], 0) { acc, x -> acc + x }
println(sums)
println(list.scan([], 0) { acc, x -> acc + x }) -- [0]
}open in playgroundlist.sort_by
list.sort_by(xs: List(a), key: Fn(a) -> b) -> List(a) where b: Compare
Returns a new list sorted by the result of applying the key function to each element.
import list
import string
fn main() {
let words = ["banana", "fig", "apple"]
let sorted = words |> list.sort_by { w -> string.length(w) }
println(sorted) -- [fig, apple, banana]
}open in playgroundlist.sum
list.sum(xs: List(Int)) -> Int
Returns the sum of all elements. Returns 0 on an empty list. Overflow
is a runtime error.
import list
fn main() {
println(list.sum([1, 2, 3, 4])) -- 10
println(list.sum([])) -- 0
}open in playgroundlist.sum_float
list.sum_float(xs: List(Float)) -> Float
Like sum, but for lists of floats. Returns 0.0 on an empty list.
Raises a runtime error when the sum is too large for a Float.
import list
fn main() {
println(list.sum_float([0.5, 1.5, 2.0])) -- 4.0
println(list.sum_float([])) -- 0.0
}open in playgroundlist.tail
list.tail(xs: List(a)) -> List(a)
Returns all elements except the first. Returns an empty list if the input is empty.
import list
fn main() {
println(list.tail([1, 2, 3])) -- [2, 3]
println(list.tail([])) -- []
}open in playgroundlist.take
list.take(xs: List(a), n: Int) -> List(a)
Returns the first n elements. If n >= length, returns the whole list.
Negative n is a runtime error.
import list
fn main() {
println(list.take([1, 2, 3, 4, 5], 3)) -- [1, 2, 3]
}open in playgroundlist.unfold
list.unfold(seed: a, f: Fn(a) -> Option((b, a))) -> List(b)
Builds a list from a seed value. The function returns Some((element, next_seed))
to emit an element and continue, or None to stop.
import list
fn main() {
let countdown = list.unfold(5) { n ->
match {
n <= 0 -> None
_ -> Some((n, n - 1))
}
}
println(countdown) -- [5, 4, 3, 2, 1]
}open in playgroundlist.unique
list.unique(xs: List(a)) -> List(a) where a: Equal
Removes duplicate elements, preserving the order of first occurrences.
import list
fn main() {
println(list.unique([1, 2, 1, 3, 2])) -- [1, 2, 3]
}open in playgroundlist.zip
list.zip(xs: List(a), ys: List(b)) -> List((a, b))
Pairs up elements from two lists. Stops at the shorter list.
import list
fn main() {
let pairs = list.zip([1, 2, 3], ["a", "b", "c"])
println(pairs) -- [(1, a), (2, b), (3, c)]
}open in playground