README

#List

The List package provides an immutable linked list data structure with a variety of utility functions for functional programming.

#Table of Contents

  1. Overview
  2. Performance
  3. Usage
    • Create
    • Basic Operations
    • Access Elements
    • Iteration
    • Advanced Operations
    • Conversion
    • Equality
  4. Error Handling Best Practices
  5. Implementation Notes
  6. Comparison with Other Collections


#Overview

List is a functional, immutable data structure that supports efficient traversal, transformation, and manipulation. It is particularly useful for recursive algorithms and scenarios where immutability is required.


#Performance

  • prepend: O(1)
  • length: O(n)
  • map/filter: O(n)
  • concatenate: O(n)
  • reverse: O(n)
  • nth: O(n)
  • sort: O(n log n)
  • flatten: O(n * m), where m is the average inner list length
  • space complexity: O(n)


#Usage

#Create

You can create an empty list or a list from an array.

///|
test {
let empty_list : @list.List[Int] = @list.new()
assert_true(empty_list.is_empty())
let list = @list.List([1, 2, 3, 4, 5])
@debug.assert_eq(list, List([1, 2, 3, 4, 5]))
}


#Basic Operations

#Prepend

Add an element to the beginning of the list.

///|
test {
let list = @list.List([2, 3, 4, 5]).prepend(1)
@debug.assert_eq(list, List([1, 2, 3, 4, 5]))
}

#Length

Get the number of elements in the list.

///|
test {
let list = @list.List([1, 2, 3, 4, 5])
@test.assert_eq(list.length(), 5)
}

#Check if Empty

Determine if the list is empty.

///|
test {
let empty_list : @list.List[Int] = @list.new()
@test.assert_eq(empty_list.is_empty(), true)
}


#Access Elements

Get the first element of the list as an Option.

///|
test {
let list = @list.List([1, 2, 3, 4, 5])
assert_true(list.head() == Some(1))
}

#Tail

Get the list without its first element.

///|
test {
let list = @list.List([1, 2, 3, 4, 5])
@debug.assert_eq(list.unsafe_tail(), List([2, 3, 4, 5]))
}

#Nth Element

Get the nth element of the list as an Option.

///|
test {
let list = @list.List([1, 2, 3, 4, 5])
assert_true(list.nth(2) == Some(3))
}


#Iteration

#Each

Iterate over the elements of the list.

///|
test {
let arr = []
@list.List([1, 2, 3, 4, 5]).each(x => arr.push(x))
@debug.assert_eq(arr, [1, 2, 3, 4, 5])
}

#Map

Transform each element of the list.

///|
test {
let list = @list.List([1, 2, 3, 4, 5]).map(x => x * 2)
@debug.assert_eq(list, List([2, 4, 6, 8, 10]))
}

#Filter & Filter Map

filter keeps elements matching a predicate. filter_map transforms and filters in one pass.

///|
test {
let list = @list.List([1, 2, 3, 4, 5])
@debug.assert_eq(list.filter(fn(x) { x % 2 == 0 }), List([2, 4]))
let fm = list.filter_map(fn(x) { if x > 3 { Some(x * 10) } else { None } })
@debug.assert_eq(fm, List([40, 50]))
}

#Fold

fold reduces from left to right. foldi includes the index.

///|
test {
let list = @list.List([1, 2, 3])
@test.assert_eq(list.fold(init=0, fn(acc, x) { acc + x }), 6)
let indexed = list.foldi(init="", fn(i, acc, x) {
acc + i.to_string() + ":" + x.to_string() + " "
})
inspect(indexed, content="0:1 1:2 2:3 ")
}

#Find, Any, All, Contains

///|
test {
let list = @list.List([1, 2, 3, 4, 5])
assert_true(list.find(fn(x) { x > 3 }) == Some(4))
assert_true(list.find_index(fn(x) { x == 3 }) == Some(2))
@test.assert_eq(list.any(fn(x) { x > 4 }), true)
@test.assert_eq(list.all(fn(x) { x > 0 }), true)
@test.assert_eq(list.contains(3), true)
@test.assert_eq(list.contains(9), false)
}

#Flat Map

///|
test {
let list = @list.List([1, 2, 3])
let result = list.flat_map(fn(x) { List([x, x * 10]) })
@debug.assert_eq(result, List([1, 10, 2, 20, 3, 30]))
}


#Advanced Operations

#Take & Drop

take(n) keeps the first n elements. drop(n) skips them. take_while and drop_while use a predicate.

///|
test {
let list = @list.List([1, 2, 3, 4, 5])
@debug.assert_eq(list.take(3), List([1, 2, 3]))
@debug.assert_eq(list.drop(3), List([4, 5]))
@debug.assert_eq(list.take_while(fn(x) { x < 4 }), List([1, 2, 3]))
@debug.assert_eq(list.drop_while(fn(x) { x < 4 }), List([4, 5]))
}

#Remove

remove(x) removes the first occurrence. remove_at(i) removes by index.

///|
test {
let list = @list.List([1, 2, 3, 2, 1])
@debug.assert_eq(list.remove(2), List([1, 3, 2, 1]))
@debug.assert_eq(list.remove_at(0), List([2, 3, 2, 1]))
}

#Last, Minimum, Maximum

///|
test {
let list = @list.List([3, 1, 4, 1, 5])
assert_true(list.last() == Some(5))
assert_true(list.minimum() == Some(1))
assert_true(list.maximum() == Some(5))
}

#Reverse

Reverse the list.

///|
test {
let list = @list.List([1, 2, 3, 4, 5]).rev()
@debug.assert_eq(list, List([5, 4, 3, 2, 1]))
}

#Concatenate

Concatenate two lists.

///|
test {
let list = @list.List([1, 2, 3]).concat(List([4, 5]))
@debug.assert_eq(list, List([1, 2, 3, 4, 5]))
}

#Flatten

Flatten a list of lists.

///|
test {
let list = @list.List([@list.List([1, 2]), List([3, 4])]).flatten()
@debug.assert_eq(list, List([1, 2, 3, 4]))
}

#Sort

Sort the list in ascending order.

///|
test {
let list = @list.List([3, 1, 4, 1, 5, 9]).sort()
@debug.assert_eq(list, List([1, 1, 3, 4, 5, 9]))
}

#Intersperse & Intercalate

intersperse inserts a separator between every pair of elements. intercalate joins a list of lists with a separator list.

///|
test {
let list = @list.List([1, 2, 3])
@debug.assert_eq(list.intersperse(0), List([1, 0, 2, 0, 3]))
let nested = @list.List([@list.List([1, 2]), List([3, 4]), List([5])])
let sep = @list.List([0])
@debug.assert_eq(nested.intercalate(sep), List([1, 2, 0, 3, 4, 0, 5]))
}

#Zip & Unzip

///|
test {
let a = @list.List([1, 2, 3])
let b = @list.List(["a", "b", "c"])
let zipped = @list.zip(a, b)
@debug.assert_eq(zipped, List([(1, "a"), (2, "b"), (3, "c")]))
let (xs, ys) = zipped.unzip()
@debug.assert_eq(xs, List([1, 2, 3]))
@debug.assert_eq(ys, List(["a", "b", "c"]))
}

#Scan

scan_left produces a list of successive fold results. scan_right does the same from right to left.

///|
test {
let list = @list.List([1, 2, 3])
@debug.assert_eq(
list.scan_left(fn(acc, x) { acc + x }, init=0),
List([0, 1, 3, 6]),
)
}

#Prefix & Suffix

///|
test {
let list = @list.List([1, 2, 3, 4, 5])
@test.assert_eq(list.is_prefix(List([1, 2, 3])), true)
@test.assert_eq(list.is_suffix(List([4, 5])), true)
}

#Lookup

Look up a value in an association list (list of key-value pairs):

///|
test {
let assoc = @list.List([("a", 1), ("b", 2), ("c", 3)])
assert_true(assoc.lookup("b") == Some(2))
assert_true(assoc.lookup("z") == None)
}

#Unfold

Build a list from a seed value. unfold produces elements in order; rev_unfold in reverse.

///|
test {
let list = @list.unfold(init=1, fn(n) {
if n > 5 {
None
} else {
Some((n, n + 1))
}
})
@debug.assert_eq(list, List([1, 2, 3, 4, 5]))
}


#Iterators

iter() returns an Iter. from_iter() constructs a list from an iterator.

///|
test {
let list = @list.List([1, 2, 3])
debug_inspect(list.iter().to_array(), content="[1, 2, 3]")
let list2 = @list.from_iter([4, 5, 6].iter())
@debug.assert_eq(list2, List([4, 5, 6]))
}


#Conversion

#To Array

Convert a list to an array.

///|
test {
let list = @list.List([1, 2, 3, 4, 5])
@debug.assert_eq(list.to_array(), [1, 2, 3, 4, 5])
}

#From Array

Create a list from an array.

///|
test {
let list = @list.List([1, 2, 3, 4, 5])
@debug.assert_eq(list, List([1, 2, 3, 4, 5]))
}


#Equality

Lists with the same elements in the same order are considered equal.

///|
test {
let list1 = @list.List([1, 2, 3])
let list2 = @list.List([1, 2, 3])
@test.assert_eq(list1 == list2, true)
}


#Error Handling Best Practices

When accessing elements that might not exist, use pattern matching for safety:

///|
fn safe_head(list : @list.List[Int]) -> Int {
match list.head() {
Some(value) => value
None => 0 // Default value
}
}

///|
test {
let list = @list.List([1, 2, 3])
@test.assert_eq(safe_head(list), 1)
let empty_list : @list.List[Int] = @list.new()
@test.assert_eq(safe_head(empty_list), 0)
}

#Additional Error Cases

  • nth() on an empty list or out-of-bounds index: Returns None.
  • tail() on an empty list: Returns Empty.
  • sort() with non-comparable elements: Throws a runtime error.


#Implementation Notes

The List is implemented as a singly linked list. Operations like prepend and head are O(1), while operations like length and map are O(n).

Key properties of the implementation:
  • Immutable by design
  • Recursive-friendly
  • Optimized for functional programming patterns


#Comparison with Other Collections

  • @array.Array: Provides O(1) random access but is mutable; use when random access is required.
  • @list.List: Immutable and optimized for recursive operations; use when immutability and functional patterns are required.

Choose List when you need:
  • Immutable data structures
  • Efficient prepend operations
  • Functional programming patterns

#
List

pub enum List[A] {
Empty
More(A, mut tail~ : List[A])
}

Type List used by this package APIs.
impl Add for List[A]
impl Compare for List[A]
impl Default for List[X]
impl Eq for List[A]
impl Hash for List[A]
impl Show for List[A]
impl ToJson for List[A]
impl FromJson for List[A]

#
List::List

fn[A] List::List(arr : ArrayView[A]) -> List[A]

test {
let lst = @list.List([1, 2, 3, 4, 5])
debug_inspect(
lst,
content=(
#|<List: [1, 2, 3, 4, 5]>
),
)
}

#
List::all

fn[A] List::all(self : List[A], f : (A) -> Bool raise?) -> Bool raise?

Test if all elements of the list satisfy the predicate.

Returns true if every element satisfies the predicate, or if the list is empty. Returns false as soon as an element that doesn't satisfy the predicate is found.

Example

test {
let ls = @list.List([2, 4, 6, 8])
@test.assert_eq(ls.all(x => x % 2 == 0), true)
let ls2 = @list.List([2, 3, 6, 8])
@test.assert_eq(ls2.all(x => x % 2 == 0), false)
}

#
List::any

fn[A] List::any(self : List[A], f : (A) -> Bool raise?) -> Bool raise?

Test if any element of the list satisfies the predicate.

Returns true as soon as an element that satisfies the predicate is found. Returns false if no element satisfies the predicate, or if the list is empty.

Example

test {
let ls = @list.List([1, 3, 5, 6])
@test.assert_eq(ls.any(x => x % 2 == 0), true)
let ls2 = @list.List([1, 3, 5, 7])
@test.assert_eq(ls2.any(x => x % 2 == 0), false)
}

#
List::compare

fn[A : Compare + Eq] List::compare(self : List[A], other : List[A]) -> Int

#
List::concat

fn[A] List::concat(self : List[A], other : List[A]) -> List[A]

Concatenate two lists.

Example

test {
let ls = @list.List([1, 2, 3, 4, 5]).concat(List([6, 7, 8, 9, 10]))
@debug.assert_eq(ls, List([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
}

#
List::cons

#as_free_fn(construct, deprecated="Use cons instead")
#as_free_fn
fn[A] List::cons(head : A, tail : List[A]) -> List[A]

Prepend an element to the list and create a new list.

This function constructs a new list with the given element as the head and the provided list as the tail.

A more familiar name of this function is cons.

Example

test {
let tail = @list.List([2, 3, 4])
let ls = @list.cons(1, tail)
@debug.assert_eq(ls, List([1, 2, 3, 4]))
}

#
List::contains

fn[A : Eq] List::contains(self : List[A], value : A) -> Bool

Check if the list contains the specified value.

Returns true if any element in the list is equal to the given value, false otherwise.

Example

test {
let ls = @list.List([1, 2, 3, 4, 5])
@test.assert_eq(ls.contains(3), true)
@test.assert_eq(ls.contains(6), false)
}

#
List::drop

fn[A] List::drop(self : List[A], n : Int) -> List[A]

Drop first n elements of the list. If the list is shorter than n, return an empty list.

Example

test {
let ls = @list.List([1, 2, 3, 4, 5])
let r = ls.drop(3)
@debug.assert_eq(r, List([4, 5]))
}

#
List::drop_while

fn[A] List::drop_while(self : List[A], p : (A) -> Bool raise?) -> List[A] raise?

Drop the longest prefix of a list of elements that satisfies a given predicate.

Example

test {
let ls = @list.List([1, 2, 3, 4])
let r = ls.drop_while(x => x < 3)
@debug.assert_eq(r, List([3, 4]))
}

#
List::each

fn[A] List::each(self : List[A], f : (A) -> Unit raise?) -> Unit raise?

Iterates over the list.

Example

test {
let arr = []
@list.List([1, 2, 3, 4, 5]).each(x => arr.push(x))
@debug.assert_eq(arr, [1, 2, 3, 4, 5])
}

#
List::eachi

fn[A] List::eachi(self : List[A], f : (Int, A) -> Unit raise?) -> Unit raise?

Iterates over the list with index.

Example

test {
let arr = []
@list.List([1, 2, 3, 4, 5]).eachi((i, x) => arr.push("(\{i},\{x})"))
@debug.assert_eq(arr, ["(0,1)", "(1,2)", "(2,3)", "(3,4)", "(4,5)"])
}

#
List::equal

fn[A : Eq] List::equal(self : List[A], other : List[A]) -> Bool

#
List::filter

fn[A] List::filter(self : List[A], f : (A) -> Bool raise?) -> List[A] raise?

Filter the list.

Example

test {
@debug.assert_eq(
@list.List([1, 2, 3, 4, 5]).filter(x => x % 2 == 0),
List([2, 4]),
)
}

#
List::filter_map

fn[A, B] List::filter_map(self : List[A], f : (A) -> B? raise?) -> List[B] raise?

Map over the list and keep all values for which the mapped result is Some(value).

Example

test {
let ls = @list.List([4, 2, 2, 6, 3, 1])
let r = ls.filter_map(x => if x >= 3 { Some(x) } else { None })
@debug.assert_eq(r, List([4, 6, 3]))
}

#
List::find

fn[A] List::find(self : List[A], f : (A) -> Bool raise?) -> A? raise?

Find the first element in the list that satisfies f.

Example

test {
assert_true(
@list.List([1, 3, 5, 8]).find(element => element % 2 == 0) == Some(8),
)
assert_true(@list.List([1, 3, 5]).find(element => element % 2 == 0) == None)
}

#
List::find_index

fn[A] List::find_index(self : List[A], f : (A) -> Bool raise?) -> Int? raise?

Returns the index of the first element in the list that satisfies the predicate function, or None if no element satisfies the predicate.

Parameters:

  • self : The input list to search through.
  • f : A function that takes an element of the list and returns a boolean indicating whether the element satisfies the search criteria.

Returns an Option containing the index of the first matching element, or None if no element matches.

Example:

test {
let ls = @list.List([1, 2, 3, 4, 5])
debug_inspect(ls.find_index(x => x % 2 == 0), content="Some(1)")
debug_inspect(ls.find_index(x => x > 10), content="None")
}

#
List::findi

fn[A] List::findi(self : List[A], f : (A, Int) -> Bool raise?) -> A? raise?

Find the first element in the list that satisfies f and passes the index as an argument.

Example

test {
assert_true(
@list.List([1, 3, 5, 8]).findi((element, index) => {
element % 2 == 0 && index == 3
}) ==
Some(8),
)
assert_true(
@list.List([1, 3, 8, 5]).findi((element, index) => {
element % 2 == 0 && index == 3
}) ==
None,
)
}

#
List::flat_map

fn[A, B] List::flat_map(self : List[A], f : (A) -> List[B] raise?) -> List[B] raise?

map over the list and concat all results.

flat_map(f, ls) equal to ls.map(f).fold(Empty, (acc, x) => acc.concat(x))))

Example

test {
let ls = @list.List([1, 2, 3])
let r = ls.flat_map(x => List([x, x * 2]))
@debug.assert_eq(r, List([1, 2, 2, 4, 3, 6]))
}

#
List::flatten

fn[A] List::flatten(self : List[List[A]]) -> List[A]

flatten a list of lists.

Example

test {
let ls = @list.List([@list.List([1, 2, 3]), List([4, 5, 6]), List([7, 8, 9])]).flatten()
@debug.assert_eq(ls, List([1, 2, 3, 4, 5, 6, 7, 8, 9]))
}

#
List::fold

fn[A, B] List::fold(self : List[A], init~ : B, f : (B, A) -> B raise?) -> B raise?

Fold the list from left.

Example

test {
let r = @list.List([1, 2, 3, 4, 5]).fold(init=0, (acc, x) => acc + x)
inspect(r, content="15")
}

#
List::foldi

fn[A, B] List::foldi(self : List[A], init~ : B, f : (Int, B, A) -> B raise?) -> B raise?

Fold the list from left with index.

Similar to fold, but the accumulator function also receives the index of the current element.

Example

test {
let ls = @list.List([10, 20, 30])
let result = ls.foldi(init=0, (i, acc, x) => acc + x * i)
inspect(result, content="80") // 0*10 + 1*20 + 2*30 = 80
}

#
List::from_array

#deprecated("Use @list.List([...]) instead")
#as_free_fn(of, deprecated="Use @list.List([...]) instead")
#alias(of, deprecated="Use @list.List([...]) instead")
#as_free_fn(deprecated="Use @list.List([...]) instead")
fn[A] List::from_array(arr : ArrayView[A]) -> List[A]

Convert array to list.

Example

test {
let ls = @list.List([1, 2, 3, 4, 5])
@debug.assert_eq(ls, List([1, 2, 3, 4, 5]))
}

#
List::from_iter

#as_free_fn(from_iterator, deprecated="Use List::from_iter instead.")
#alias(from_iterator, deprecated="`from_iterator` is deprecated, use `from_iter` instead")
#as_free_fn
fn[A] List::from_iter(iter : Iter[A]) -> List[A]

Convert an iterator into a list, preserving order of elements.

Creates a list from an iterator, maintaining the same order as the iterator. If order is not important, consider using from_iter_rev for better performance.

Example

test {
let arr = [1, 2, 3, 4, 5]
let iter = arr.iter()
let ls = @list.from_iter(iter)
@debug.assert_eq(ls, List([1, 2, 3, 4, 5]))
}

#
List::from_iter_rev

#as_free_fn(from_iterator_rev, deprecated="Use List::from_iter_rev instead.")
#alias(from_iterator_rev, deprecated="`from_iterator_rev` is deprecated, use `from_iter_rev` instead")
#as_free_fn
fn[A] List::from_iter_rev(iter : Iter[A]) -> List[A]

Convert an iterator into a list in reverse order.

Creates a list from an iterator, but the resulting list will have elements in reverse order compared to the iterator. This is more efficient than from_iterator when order doesn't matter.

Example

test {
let arr = [1, 2, 3, 4, 5]
let iter = arr.iter()
let ls = @list.from_iter_rev(iter)
@debug.assert_eq(ls, List([5, 4, 3, 2, 1]))
}

#
List::from_json

Parse JSON into a list.

Converts a JSON array into a list of the specified type.

#
List::hash

fn[A : Hash] List::hash(self : List[A]) -> Int

#
List::head

fn[A] List::head(self : List[A]) -> A?

Get first element of the list.

Example

test {
assert_true(@list.List([1, 2, 3, 4, 5]).head() == Some(1))
}

#
List::intercalate

fn[A] List::intercalate(self : List[List[A]], sep : List[A]) -> List[A]

Insert separator lists between lists and flatten the result.

Similar to intersperse, but works with lists of lists. Inserts the separator list between each list in the input, then flattens everything into a single list.

Example

test {
let ls = @list.List([@list.List([1, 2, 3]), List([4, 5, 6]), List([7, 8, 9])])
let r = ls.intercalate(List([0]))
@debug.assert_eq(r, List([1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9]))
}

#
List::intersperse

fn[A] List::intersperse(self : List[A], separator : A) -> List[A]

Insert separator to the list.

Example

test {
let ls = @list.List(["1", "2", "3", "4", "5"]).intersperse("|")
@debug.assert_eq(ls, List(["1", "|", "2", "|", "3", "|", "4", "|", "5"]))
}

#
List::is_empty

fn[A] List::is_empty(self : List[A]) -> Bool

Check if the list is empty.

Returns true if the list contains no elements, false otherwise.

Example

test {
let empty_list : @list.List[Int] = @list.empty()
@test.assert_eq(empty_list.is_empty(), true)
let non_empty = @list.List([1, 2, 3])
@test.assert_eq(non_empty.is_empty(), false)
}

#
List::is_prefix

fn[A : Eq] List::is_prefix(self : List[A], prefix : List[A]) -> Bool

Returns true if list starts with prefix.

Example

test {
@test.assert_eq(@list.List([1, 2, 3, 4, 5]).is_prefix(List([1, 2, 3])), true)
}

#
List::is_suffix

fn[A : Eq] List::is_suffix(self : List[A], suffix : List[A]) -> Bool

Returns true if list ends with suffix.

Example

test {
@test.assert_eq(@list.List([1, 2, 3, 4, 5]).is_suffix(List([3, 4, 5])), true)
}

#
List::iter

#alias(iterator, deprecated="`iterator` is deprecated, use `iter` instead")
fn[A] List::iter(self : List[A]) -> Iter[A]

Create an iterator over the list elements.

Returns an iterator that yields each element of the list in order.

Example

test {
let ls = @list.List([1, 2, 3, 4, 5])
let iter = ls.iter()
let sum = iter.fold(init=0, (acc, x) => acc + x)
inspect(sum, content="15")
}

#
List::iter2

#alias(iterator2, deprecated="`iterator2` is deprecated, use `iter2` instead")
fn[A] List::iter2(self : List[A]) -> Iter2[Int, A]

Create an iterator over the list elements with indices.

Returns an iterator that yields pairs of (index, element) for each element in the list.

Example

test {
let ls = @list.List([10, 20, 30])
let iter = ls.iter2()
debug_inspect(iter.to_array(), content="[(0, 10), (1, 20), (2, 30)]")
}

#
List::last

fn[A] List::last(self : List[A]) -> A?

Last element of the list.

Example

test {
assert_true(@list.List([1, 2, 3, 4, 5]).last() == Some(5))
}

#
List::length

fn[A] List::length(self : List[A]) -> Int

Get the length of the list.

#
List::lookup

fn[A : Eq, B] List::lookup(self : List[(A, B)], v : A) -> B?

Looks up a key in an association list.

Example

test {
let ls = @list.List([(1, "a"), (2, "b"), (3, "c")])
assert_true(ls.lookup(3) == Some("c"))
}

#
List::map

fn[A, B] List::map(self : List[A], f : (A) -> B raise?) -> List[B] raise?

Maps the list.

Example

test {
@debug.assert_eq(
@list.List([1, 2, 3, 4, 5]).map(x => x * 2),
List([2, 4, 6, 8, 10]),
)
}

#
List::mapi

fn[A, B] List::mapi(self : List[A], f : (Int, A) -> B raise?) -> List[B] raise?

Maps the list with index.

Applies a function to each element and its index, creating a new list with the results.

Example

test {
let ls = @list.List([10, 20, 30])
let result = ls.mapi((i, x) => x + i)
@debug.assert_eq(result, List([10, 21, 32]))
}

#
List::maximum

fn[A : Compare + Eq] List::maximum(self : List[A]) -> A?

Get the maximum element of the list.

Returns Some(element) with the largest element, or None if the list is empty. Elements are compared using the Compare trait.

Example

test {
let ls = @list.List([1, 3, 2, 5, 4])
assert_true(ls.maximum() == Some(5))
let empty : @list.List[Int] = @list.empty()
assert_true(empty.maximum() == None)
}

#
List::minimum

fn[A : Compare + Eq] List::minimum(self : List[A]) -> A?

Get the minimum element of the list.

Returns Some(element) with the smallest element, or None if the list is empty. Elements are compared using the Compare trait.

Example

test {
let ls = @list.List([1, 3, 2, 5, 4])
assert_true(ls.minimum() == Some(1))
let empty : @list.List[Int] = @list.empty()
assert_true(empty.minimum() == None)
}

#
List::new

#as_free_fn(empty)
#as_free_fn
#alias(empty)
fn[A] List::new() -> List[A]

Creates an empty list.

Example

test {
let ls : @list.List[Int] = @list.new()
@debug.assert_eq(ls, @list.empty())
}

#
List::nth

fn[A] List::nth(self : List[A], n : Int) -> A?

Get the nth element of the list.

Returns Some(element) if the index is valid, or None if the index is out of bounds.

Example

test {
let ls = @list.List([1, 2, 3, 4, 5])
assert_true(ls.nth(2) == Some(3))
assert_true(ls.nth(10) == None)
}

#
List::prepend

#alias(add)
fn[A] List::prepend(self : List[A], head : A) -> List[A]

Prepend an element to the front of the list.

Creates a new list with the given element added to the beginning.

Example

test {
let ls = @list.List([2, 3, 4]).prepend(1)
@debug.assert_eq(ls, List([1, 2, 3, 4]))
}

#
List::remove

fn[A : Eq] List::remove(self : List[A], elem : A) -> List[A]

Removes the first occurrence of the specified element from the list, if it is present.

Example

test {
@debug.assert_eq(@list.List([1, 2, 3, 4, 5]).remove(3), List([1, 2, 4, 5]))
}

#
List::remove_at

fn[A] List::remove_at(self : List[A], index : Int) -> List[A]

Removes the element at the specified index in the list.

Example

test {
@debug.assert_eq(@list.List([1, 2, 3, 4, 5]).remove_at(2), List([1, 2, 4, 5]))
}

#
List::repeat

#as_free_fn
fn[A] List::repeat(n : Int, x : A) -> List[A]

Create a list of length n with the given value.

Aborts if n is negative. When n is 0, returns the empty list.

Example

test {
@debug.assert_eq(@list.repeat(5, 1), List([1, 1, 1, 1, 1]))
}

#
List::rev

fn[A] List::rev(self : List[A]) -> List[A]

Reverse the list.

Example

test {
@debug.assert_eq(@list.List([1, 2, 3, 4, 5]).rev(), List([5, 4, 3, 2, 1]))
}

#
List::rev_concat

fn[A] List::rev_concat(self : List[A], other : List[A]) -> List[A]

Reverse the first list and concatenate it with the second.

Example

test {
let ls = @list.List([1, 2, 3, 4, 5]).rev_concat(List([6, 7, 8, 9, 10]))
@debug.assert_eq(ls, List([5, 4, 3, 2, 1, 6, 7, 8, 9, 10]))
}

#
List::rev_fold

#deprecated("use `_.to_array().rev_fold(...)` instead")
fn[A, B] List::rev_fold(self : List[A], init~ : B, f : (B, A) -> B) -> B

Function rev_fold.

#
List::rev_foldi

#deprecated("use `_.rev().foldi(...)` instead")
fn[A, B] List::rev_foldi(self : List[A], init~ : B, f : (Int, B, A) -> B) -> B

Function rev_foldi.

#
List::rev_map

fn[A, B] List::rev_map(self : List[A], f : (A) -> B raise?) -> List[B] raise?

Maps the list and reverses the result.

list.rev_map(f) is equivalent to list.map(f).rev() but more efficient.

Example

test {
@debug.assert_eq(
@list.List([1, 2, 3, 4, 5]).rev_map(x => x * 2),
List([10, 8, 6, 4, 2]),
)
}

#
List::rev_unfold

#as_free_fn
fn[A, S] List::rev_unfold(f : (S) -> (A, S)? raise?, init~ : S) -> List[A] raise?

Produces a list iteratively in reverse order.

Similar to unfold, but the resulting list will be in reverse order compared to the generation order. This can be more efficient when you don't need to preserve the generation order.

Example

test {
let r = @list.rev_unfold(
i => if i == 3 { None } else { Some((i, i + 1)) },
init=0,
)
@debug.assert_eq(r, List([2, 1, 0]))
}

#
List::scan_left

fn[A, E] List::scan_left(self : List[A], f : (E, A) -> E raise?, init~ : E) -> List[E] raise?

Fold a list and return a list of successive reduced values from the left

Example

test {
let ls = @list.List([1, 2, 3, 4, 5])
let r = ls.scan_left((acc, x) => acc + x, init=0)
@debug.assert_eq(r, List([0, 1, 3, 6, 10, 15]))
}

#
List::scan_right

fn[A, B] List::scan_right(self : List[A], f : (B, A) -> B raise?, init~ : B) -> List[B] raise?

Fold a list and return a list of successive reduced values from the right

Note that the order of parameters on the accumulating function are reversed.

Example

test {
let ls = @list.List([1, 2, 3, 4, 5])
let r = ls.scan_right((acc, x) => acc + x, init=0)
@debug.assert_eq(r, List([15, 14, 12, 9, 5, 0]))
}

#
List::singleton

#as_free_fn
fn[A] List::singleton(x : A) -> List[A]

Create a list with a single element.

Returns a list containing only the given element.

Example

test {
let ls = @list.singleton(42)
@debug.assert_eq(ls, List([42]))
@test.assert_eq(ls.length(), 1)
}

#
List::sort

fn[A : Compare + Eq] List::sort(self : List[A]) -> List[A]

Sort the list in ascending order.

Example

test {
let ls = @list.List([1, 123, 52, 3, 6, 0, -6, -76]).sort()
@debug.assert_eq(ls, List([-76, -6, 0, 1, 3, 6, 52, 123]))
}

#
List::tail

#deprecated("use `unsafe_tail` instead")
fn[A] List::tail(self : List[A]) -> List[A]

Function tail.

#
List::take

fn[A] List::take(self : List[A], n : Int) -> List[A]

Take first n elements of the list. If the list is shorter than n, return the whole list.

Example

test {
let ls = @list.List([1, 2, 3, 4, 5])
let r = ls.take(3)
@debug.assert_eq(r, List([1, 2, 3]))
}

#
List::take_while

fn[A] List::take_while(self : List[A], p : (A) -> Bool raise?) -> List[A] raise?

Take the longest prefix of a list of elements that satisfies a given predicate.

Example

test {
let ls = @list.List([1, 2, 3, 4])
let r = ls.take_while(x => x < 3)
@debug.assert_eq(r, List([1, 2]))
}

#
List::to_array

fn[A] List::to_array(self : List[A]) -> Array[A]

Convert list to array.

Creates a new array containing all elements from the list in the same order.

Example

test {
let ls = @list.List([1, 2, 3, 4, 5])
let arr = ls.to_array()
@debug.assert_eq(arr, [1, 2, 3, 4, 5])
}

#
List::to_json

fn[A : ToJson] List::to_json(self : List[A]) -> Json

Convert a list to JSON.

Example

test {
let ls = @list.List([1, 2, 3])
let json = ls.to_json()
@debug.debug_inspect(json, content="Array([Number(1), Number(2), Number(3)])")
}

#
List::unfold

#as_free_fn
fn[A, S] List::unfold(f : (S) -> (A, S)? raise?, init~ : S) -> List[A] raise?

Produces a collection iteratively.

Example

test {
let r = @list.unfold(init=0, i => if i == 3 { None } else { Some((i, i + 1)) })
@debug.assert_eq(r, List([0, 1, 2]))
}

#
List::unsafe_tail

fn[A] List::unsafe_tail(self : List[A]) -> List[A]

Get the tail (all elements except the first) of the list.

Warning: This function panics if the list is empty. Use pattern matching or other safe methods for empty lists.

Example

test {
let ls = @list.List([1, 2, 3, 4, 5])
let tail = ls.unsafe_tail()
@debug.assert_eq(tail, List([2, 3, 4, 5]))
}

Panics

Panics if the list is empty.

#
List::unzip

fn[A, B] List::unzip(self : List[(A, B)]) -> (List[A], List[B])

Unzip two lists.

Example

test {
let (a, b) = @list.List([(1, 2), (3, 4), (5, 6)]).unzip()
@debug.assert_eq(a, List([1, 3, 5]))
@debug.assert_eq(b, List([2, 4, 6]))
}

#
List::zip

#as_free_fn
fn[A, B] List::zip(self : List[A], other : List[B]) -> List[(A, B)]

Zip two lists together into a list of tuples.

Combines elements from two lists pairwise. If the lists have different lengths, the result will have the length of the shorter list.

Example

test {
let r = @list.zip(List([1, 2, 3, 4, 5]), List([6, 7, 8, 9, 10]))
@debug.assert_eq(r, List([(1, 6), (2, 7), (3, 8), (4, 9), (5, 10)]))
let r2 = @list.zip(List([1, 2]), List([6, 7, 8, 9, 10]))
@debug.assert_eq(r2, List([(1, 6), (2, 7)]))
}

#
default

fn[X] default() -> List[X]

Return the default value for a list (empty list).

Example

test {
let ls : @list.List[Int] = @list.default()
@test.assert_eq(ls.is_empty(), true)
}