Seq
The Seq module provides lazy sequences, inspired by Clojure. Sequences are computed on-demand, allowing you to work with potentially infinite data structures efficiently.
Lazy Evaluation
Sequences only compute values when needed. This means you can define infinite sequences and only realize the parts you need.
Creating Sequences
Seq.empty
Seq a
An empty sequence.
Seq.singleton
a -> Seq a
Creates a sequence with a single element.
s = Seq.singleton 42
Seq.from-list
List a -> Seq a
Creates a sequence from a list.
s = Seq.from-list [1, 2, 3]
Seq.cons
a -> Seq a -> Seq a
Adds an element to the front of a sequence.
s = Seq.from-list [2, 3, 4]
s2 = 1 |> Seq.cons s
# => Seq [1, 2, 3, 4]
Seq.lazy-cons
a -> (() -> Seq a) -> Seq a
Lazily constructs a sequence by adding an element to the front of a lazily-computed tail.
fibonacci = fn(a, b) =>
Seq.lazy-cons a (fn => fibonacci b (a + b))
fibs = fibonacci 0 1
first-10 = fibs |> Seq.take 10 |> Seq.to-list
# => [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Seq.unfold
(b -> Option (a, b)) -> b -> Seq a
Generates a sequence from a seed value using a function that produces the next element and state.
countdown = Seq.unfold (fn(n) =>
if n < 0
then None
else Some (n, n - 1)
) 5
result = countdown |> Seq.to-list
# => [5, 4, 3, 2, 1, 0]
Infinite Sequences
Seq.repeat
a -> Seq a
Creates an infinite sequence of a repeated value.
ones = Seq.repeat 1
first-ten = ones |> Seq.take 10 |> Seq.to-list
# => [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
Seq.iterate
(a -> a) -> a -> Seq a
Creates an infinite sequence by repeatedly applying a function.
powers-of-2 = Seq.iterate (fn(x) => x * 2) 1
first-10 = powers-of-2 |> Seq.take 10 |> Seq.to-list
# => [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
Seq.range-from
Int -> Seq Int
Creates an infinite sequence of integers starting from a value.
naturals = Seq.range-from 1
first-5 = naturals |> Seq.take 5 |> Seq.to-list
# => [1, 2, 3, 4, 5]
Seq.cycle
List a -> Seq a
Creates an infinite sequence by cycling through a list.
colors = Seq.cycle ["red", "green", "blue"]
first-7 = colors |> Seq.take 7 |> Seq.to-list
# => ["red", "green", "blue", "red", "green", "blue", "red"]
Accessing Elements
Seq.is-empty?
Seq a -> Bool
Checks if a sequence is empty.
empty? = Seq.empty |> Seq.is-empty?
# => true
not-empty? = Seq.singleton 1 |> Seq.is-empty?
# => false
Seq.rest
Seq a -> Seq a
Returns all elements except the first one.
s = Seq.from-list [1, 2, 3, 4]
tail = Seq.rest s |> Seq.to-list
# => [2, 3, 4]
Seq.nth
Int -> Seq a -> Option a
Gets the element at the given index (0-based), if it exists.
s = Seq.from-list ["a", "b", "c"]
second = 1 |> Seq.nth s
# => Some "b"
out-of-bounds = 10 |> Seq.nth s
# => None
Transforming Sequences
Seq.map
(a -> b) -> Seq a -> Seq b
Lazily transforms each element.
Seq.filter
(a -> Bool) -> Seq a -> Seq a
Lazily filters elements.
Seq.take
Int -> Seq a -> Seq a
Takes the first n elements.
Seq.drop
Int -> Seq a -> Seq a
Drops the first n elements.
Seq.take-while
(a -> Bool) -> Seq a -> Seq a
Takes elements while the predicate is true.
nums = Seq.from-list [2, 4, 6, 7, 8]
evens = (fn(x) => x % 2 == 0) |> Seq.take-while nums |> Seq.to-list
# => [2, 4, 6]
Seq.drop-while
(a -> Bool) -> Seq a -> Seq a
Drops elements while the predicate is true.
nums = Seq.from-list [2, 4, 6, 7, 8]
rest = (fn(x) => x % 2 == 0) |> Seq.drop-while nums |> Seq.to-list
# => [7, 8]
Seq.flat-map
(a -> Seq b) -> Seq a -> Seq b
Maps each element to a sequence and flattens the results.
duplicate = fn(x) => Seq.from-list [x, x]
nums = Seq.from-list [1, 2, 3]
result = duplicate |> Seq.flat-map nums |> Seq.to-list
# => [1, 1, 2, 2, 3, 3]
Seq.flatten
Seq (Seq a) -> Seq a
Flattens a sequence of sequences into a single sequence.
nested = Seq.from-list [
Seq.from-list [1, 2],
Seq.from-list [3, 4],
Seq.from-list [5]
]
flat = Seq.flatten nested |> Seq.to-list
# => [1, 2, 3, 4, 5]
Seq.dedupe
Seq a -> Seq a
Removes consecutive duplicate elements.
s = Seq.from-list [1, 1, 2, 2, 2, 3, 1]
deduped = Seq.dedupe s |> Seq.to-list
# => [1, 2, 3, 1]
Seq.distinct
Seq a -> Seq a
Removes all duplicate elements (keeps first occurrence).
s = Seq.from-list [1, 2, 3, 2, 1, 4]
unique = Seq.distinct s |> Seq.to-list
# => [1, 2, 3, 4]
Combining Sequences
Seq.concat
Seq a -> Seq a -> Seq a
Concatenates two sequences.
s1 = Seq.from-list [1, 2]
s2 = Seq.from-list [3, 4]
combined = Seq.concat s1 s2 |> Seq.to-list
# => [1, 2, 3, 4]
Seq.interleave
Seq a -> Seq a -> Seq a
Interleaves two sequences, alternating elements from each.
s1 = Seq.from-list [1, 2, 3]
s2 = Seq.from-list ["a", "b", "c"]
interleaved = Seq.interleave s1 s2 |> Seq.to-list
# => [1, "a", 2, "b", 3, "c"]
Seq.interpose
a -> Seq a -> Seq a
Inserts a separator element between all elements of the sequence.
words = Seq.from-list ["hello", "world", "!"]
spaced = " " |> Seq.interpose words |> Seq.to-list
# => ["hello", " ", "world", " ", "!"]
Seq.zip-with
(a -> b -> c) -> Seq a -> Seq b -> Seq c
Combines two sequences element-wise using a function.
s1 = Seq.from-list [1, 2, 3]
s2 = Seq.from-list [10, 20, 30]
sums = Seq.zip-with (fn(a, b) => a + b) s1 s2 |> Seq.to-list
# => [11, 22, 33]
Filtering & Partitioning
Seq.partition
Int -> Seq a -> Seq (List a)
Splits a sequence into chunks of a given size.
nums = Seq.from-list [1, 2, 3, 4, 5, 6]
pairs = 2 |> Seq.partition nums |> Seq.to-list
# => [[1, 2], [3, 4], [5, 6]]
Seq.partition-by
(a -> b) -> Seq a -> Seq (List a)
Splits a sequence into chunks whenever the function result changes.
nums = Seq.from-list [1, 2, 2, 3, 3, 3, 4]
grouped = (fn(x) => x) |> Seq.partition-by nums |> Seq.to-list
# => [[1], [2, 2], [3, 3, 3], [4]]
Realizing Sequences
Seq.to-list
Seq a -> List a
Realizes the entire sequence into a list. Be careful with infinite sequences.
Seq.realize
Int -> Seq a -> List a
Realizes up to n elements into a list. Safe for infinite sequences.
naturals = Seq.range-from 1
first-100 = Seq.realize 100 naturals
Seq.first
Seq a -> Option a
Returns the first element, if any.
s = Seq.from-list [1, 2, 3]
first = Seq.first s
# => Some 1
Seq.fold
(b -> a -> b) -> b -> Seq a -> b
Folds the sequence from left to right using an accumulator function.
nums = Seq.from-list [1, 2, 3, 4]
sum = Seq.fold (fn(acc, x) => acc + x) 0 nums
# => 10
Seq.find
(a -> Bool) -> Seq a -> Option a
Finds the first element that satisfies the predicate.
nums = Seq.from-list [1, 2, 3, 4, 5]
first-even = (fn(x) => x % 2 == 0) |> Seq.find nums
# => Some 2
Seq.any?
(a -> Bool) -> Seq a -> Bool
Checks if any element satisfies the predicate.
nums = Seq.from-list [1, 3, 5, 7]
has-even? = (fn(x) => x % 2 == 0) |> Seq.any? nums
# => false
has-odd? = (fn(x) => x % 2 == 1) |> Seq.any? nums
# => true
Seq.all?
(a -> Bool) -> Seq a -> Bool
Checks if all elements satisfy the predicate.
nums = Seq.from-list [2, 4, 6, 8]
all-even? = (fn(x) => x % 2 == 0) |> Seq.all? nums
# => true
all-positive? = (fn(x) => x > 0) |> Seq.all? nums
# => true