List
The List module provides functions for working with immutable, ordered sequences of elements. Lists are one of the most commonly used data structures in Kit.
Most list functions are available both as bare functions (map, filter)
and with the module prefix (List.map, List.filter).
Important: Some accessor functions differ between bare and module forms:
bare head, first, last return the element directly (raising an error on empty lists),
while List.head, List.first, List.last return Option a for safe access.
Creating Lists
numbers = [1, 2, 3, 4, 5]
names = ["Alice", "Bob", "Carol"]
empty = []
[].
empty = List.empty
println (List.is-empty? empty)
# => true
start to end (exclusive).
Also available as the built-in vec-range.
nums = List.range 1 6
# => [1, 2, 3, 4, 5]
evens = List.range 0 10 |> filter (fn(x) => x % 2 == 0)
# => [0, 2, 4, 6, 8]
n copies of a value.
zeros = List.replicate 5 0
# => [0, 0, 0, 0, 0]
greetings = List.replicate 3 "hello"
# => ["hello", "hello", "hello"]
Accessing Elements
println (head [1, 2, 3])
# => 1
Some first element of a list, or None if empty. Safe version of head.
println (List.head [1, 2, 3])
# => Some(1)
println (List.head [])
# => None
Some first element of a list, or None if empty. Alias for List.head.
println (List.first ["a", "b"])
# => Some("a")
rest and List.rest.
println (tail [1, 2, 3])
# => [2, 3]
println (List.second [1, 2, 3])
# => 2
println (last [1, 2, 3])
# => 3
Some last element of a list, or None if empty. Safe version of last.
println (List.last [1, 2, 3])
# => Some(3)
println (List.last [])
# => None
Some element at the given index (0-based), or None if out of bounds.
items = ["a", "b", "c"]
println (nth 1 items)
# => Some("b")
println (nth 10 items)
# => None
Some element at the given index (0-based), or None if out of bounds.
Alias for nth.
println (List.get 0 [10, 20, 30])
# => Some(10)
println (List.set 1 "X" ["a", "b", "c"])
# => ["a", "X", "c"]
tail.
println (List.init [1, 2, 3, 4])
# => [1, 2, 3]
len and count.
println (length [1, 2, 3, 4, 5])
# => 5
true if the list has no elements.
println (empty? []) # => true
println (empty? [1, 2]) # => false
true if the list has no elements. Alias for empty?.
println (List.is-empty? []) # => true
println (List.is-empty? [1, 2]) # => false
Transforming Lists
doubled = map (fn(x) => x * 2) [1, 2, 3]
# => [2, 4, 6]
# With pipe operator
result = [1, 2, 3] |> map (fn(x) => x * x)
# => [1, 4, 9]
println (reverse [1, 2, 3])
# => [3, 2, 1]
println (List.sort [3, 1, 4, 1, 5])
# => [1, 1, 3, 4, 5]
flatten (map f xs).
result = List.flat-map (fn(x) => [x, x * 10]) [1, 2, 3]
# => [1, 10, 2, 20, 3, 30]
fold, but returns a list of all intermediate accumulator values.
result = List.scan (fn(acc, x) => acc + x) 0 [1, 2, 3]
# => [1, 3, 6] (running sums)
println (List.intersperse 0 [1, 2, 3])
# => [1, 0, 2, 0, 3]
println (List.interleave [1, 2, 3] [10, 20, 30])
# => [1, 10, 2, 20, 3, 30]
Filtering
true.
evens = filter (fn(x) => x % 2 == 0) [1, 2, 3, 4, 5]
# => [2, 4]
positives = [-2, -1, 0, 1, 2] |> filter (fn(x) => x > 0)
# => [1, 2]
n elements of a list.
println (take 3 [1, 2, 3, 4, 5])
# => [1, 2, 3]
n elements.
println (drop 2 [1, 2, 3, 4, 5])
# => [3, 4, 5]
println (List.slice-from 2 [1, 2, 3, 4, 5])
# => [3, 4, 5]
items = ["a", "b", "c", "d"]
println (List.slice-from 1 items)
# => ["b", "c", "d"]
println (List.slice 1 4 [0, 1, 2, 3, 4])
# => [1, 2, 3]
println (List.take-while (fn(x) => x < 4) [1, 2, 3, 5, 2])
# => [1, 2, 3]
println (List.drop-while (fn(x) => x < 4) [1, 2, 3, 5, 2])
# => [5, 2]
n elements of a list.
println (List.take-last 2 [1, 2, 3, 4, 5])
# => [4, 5]
n elements.
println (List.drop-last 2 [1, 2, 3, 4, 5])
# => [1, 2, 3]
Reducing
# Sum all numbers
total = fold (fn(acc, x) => acc + x) 0 [1, 2, 3, 4, 5]
# => 15
# Build a string
joined = fold (fn(acc, s) => acc ++ s) "" ["a", "b", "c"]
# => "abc"
fold, but uses the first element as the initial accumulator.
product = reduce (fn(a, b) => a * b) [1, 2, 3, 4]
# => 24
largest = reduce max [3, 1, 4, 1, 5]
# => 5
println (sum [1, 2, 3, 4, 5])
# => 15
println (product [1, 2, 3, 4])
# => 24
counts = List.frequencies [1, 2, 1, 3, 2, 1]
# => {1: 3, 2: 2, 3: 1}
word-counts = List.frequencies ["apple", "banana", "apple", "cherry"]
# => {"apple": 2, "banana": 1, "cherry": 1}
Combining Lists
++ operator is an alias.
combined = concat [1, 2] [3, 4]
# => [1, 2, 3, 4]
combined2 = [1, 2] ++ [3, 4]
# => [1, 2, 3, 4]
result = cons 0 [1, 2, 3]
# => [0, 1, 2, 3]
result = append [1, 2, 3] 4
# => [1, 2, 3, 4]
nested = [[1, 2], [3, 4], [5]]
println (flatten nested)
# => [1, 2, 3, 4, 5]
sums = zip-with (fn(a, b) => a + b) [1, 2, 3] [10, 20, 30]
# => [11, 22, 33]
println (List.zip [1, 2, 3] ["a", "b", "c"])
# => [(1, "a"), (2, "b"), (3, "c")]
List.zip.
(nums, strs) = List.unzip [(1, "a"), (2, "b"), (3, "c")]
# nums => [1, 2, 3]
# strs => ["a", "b", "c"]
numbers = [1, 2, 3]
letters = ["a", "b", "c"]
bools = [true, false, true]
result = List.zip3 numbers letters bools
# => [(1, "a", true), (2, "b", false), (3, "c", true)]
Searching
true if the list contains the given element.
println (contains? 3 [1, 2, 3, 4])
# => true
println (contains? 5 [1, 2, 3, 4])
# => false
[1, 2, 3] |> each (fn(x) => println x)
# Prints: 1, 2, 3 (each on new line)
None if none match.
result = List.find (fn(x) => x > 3) [1, 2, 5, 4]
# => Some(5)
result = List.find (fn(x) => x > 10) [1, 2, 3]
# => None
None if none match.
result = List.find-index (fn(x) => x > 3) [1, 2, 5, 4]
# => Some(2)
users = [{name: "Alice", id: 1}, {name: "Bob", id: 2}]
result = List.find-by (fn(u) => u.id) 2 users
# => Some({name: "Bob", id: 2})
true if any element matches the predicate.
println (List.any? (fn(x) => x > 3) [1, 2, 5])
# => true
println (List.any? (fn(x) => x > 10) [1, 2, 3])
# => false
true if all elements match the predicate.
println (List.all? (fn(x) => x > 0) [1, 2, 3])
# => true
println (List.all? (fn(x) => x > 2) [1, 2, 3])
# => false
count which returns list length.
println (List.count (fn(x) => x > 2) [1, 2, 3, 4, 5])
# => 3
Some index if found, None otherwise.
sorted = [1, 3, 5, 7, 9]
println (List.binary-search 5 sorted)
# => Some(2)
Some index if found, None otherwise.
users = [{id: 1, name: "Alice"}, {id: 3, name: "Bob"}, {id: 5, name: "Carol"}]
println (List.binary-search-by (fn(u) => u.id) 3 users)
# => Some(1)
sorted = [1, 3, 3, 5, 7]
println (List.lower-bound 3 sorted)
# => 1
sorted = [1, 3, 3, 5, 7]
println (List.upper-bound 3 sorted)
# => 3
Sorting
users = [{name: "Bob"}, {name: "Alice"}]
sorted = List.sort-by (fn(u) => u.name) users
# => [{name: "Alice"}, {name: "Bob"}]
# Sort descending
desc = List.sort-with (fn(a, b) => b - a) [3, 1, 4]
# => [4, 3, 1]
None if the list is empty.
println (List.minimum [3, 1, 4])
# => Some(1)
println (List.minimum [])
# => None
None if the list is empty.
println (List.maximum [3, 1, 4])
# => Some(4)
None if empty.
users = [{name: "Alice", age: 30}, {name: "Bob", age: 25}]
youngest = List.min-by (fn(u) => u.age) users
# => Some({name: "Bob", age: 25})
None if empty.
users = [{name: "Alice", age: 30}, {name: "Bob", age: 25}]
oldest = List.max-by (fn(u) => u.age) users
# => Some({name: "Alice", age: 30})
None if empty.
items = ["banana", "apple", "cherry"]
shortest = List.min-with (fn(a, b) => length(a) - length(b)) items
# => Some("apple")
None if empty.
items = ["banana", "apple", "cherry"]
longest = List.max-with (fn(a, b) => length(a) - length(b)) items
# => Some("banana")
Partitioning
(evens, odds) = List.partition (fn(x) => x % 2 == 0) [1, 2, 3, 4, 5]
# evens => [2, 4]
# odds => [1, 3, 5]
(before, after) = List.split-at 2 [1, 2, 3, 4, 5]
# before => [1, 2]
# after => [3, 4, 5]
println (List.unique [1, 2, 1, 3, 2])
# => [1, 2, 3]
users = [{name: "Alice", dept: "A"}, {name: "Bob", dept: "A"}]
unique-depts = List.unique-by (fn(u) => u.dept) users
# => [{name: "Alice", dept: "A"}]
items = [{type: "a", val: 1}, {type: "b", val: 2}, {type: "a", val: 3}]
grouped = List.group-by (fn(x) => x.type) items
# => [[{type: "a", val: 1}, {type: "a", val: 3}], [{type: "b", val: 2}]]
result = List.partition-by (fn(x) => x % 2) [1, 3, 2, 4, 5]
# => [[1, 3], [2, 4], [5]]
Built-ins
These functions are available without a module prefix.
head.
println (first ["a", "b", "c"])
# => "a"
tail.
println (rest ["a", "b", "c"])
# => ["b", "c"]
length for lists.
println (len ["a", "b", "c"])
# => 3
len.
println (count [10, 20])
# => 2
start to end (exclusive). Equivalent to List.range.
println (vec-range 0 5)
# => [0, 1, 2, 3, 4]