March Docs

Enum

Enum module: Elixir-style enumeration over List(a).

All functions operate on List(a). The Iterable interface (when runtime dispatch lands) will generalise these to any collection.

Comparator convention: cmp : a -> a -> Bool is curried; cmp(x)(y) = true means x should come before y (x is "less than or equal to" y). Write it as fn a -> fn b -> a <= b; an uncurried fn (a, b) -> ... fails at runtime. Callbacks of fold, reduce, scan and zip_with are curried the same way.

See specs/2026-03-19-sorting-and-enum-design.md for design rationale.

Functions

fnallall(xs : List(a), pred : a -> Bool) : Bool#

Returns true if all elements satisfy pred. Short-circuits on first failure. O(n).

Use all to validate a collection without counting or filtering. True for an empty list.

Examples

march> Enum.all([1, 2, 3], fn x -> x > 2)
false
march> Enum.all([], fn x -> x > 2)
true
fnanyany(xs : List(a), pred : a -> Bool) : Bool#

Returns true if at least one element satisfies pred. Short-circuits. O(n).

Use any instead of filter + count when you only need a yes/no answer — any stops at the first match. False for an empty list.

Examples

march> Enum.any([1, 2, 3], fn x -> x > 2)
true
march> Enum.any([], fn x -> x > 2)
false
fnchunk_bychunk_by(xs : List(a), key : a -> k) : List((k, List(a)))#

Chunks consecutive elements with the same key into (key, [elements]) pairs. O(n). Only CONSECUTIVE equal-key runs are grouped.

This is identical to group_by but named correctly after Elixir's chunk_by. Use chunk_by when the grouping key changes only at boundaries (e.g. run-length encoding). For grouping all occurrences of a key, use frequencies/assoc lists.

Examples

march> Enum.chunk_by([1, 1, 2, 1], fn x -> x % 2)
[(1, [1, 1]), (0, [2]), (1, [1])]
fnchunk_everychunk_every(xs : List(a), n : {Int | _ > 0}) : List(List(a))#

Split xs into consecutive chunks of exactly n elements each. The final chunk may be smaller than n if len(xs) is not divisible by n. Returns [] for empty xs. O(n).

n must be positive.

Examples

march> Enum.chunk_every([1, 2, 3, 4, 5], 2)
[[1, 2], [3, 4], [5]]
march> Enum.chunk_every([], 2)
[]
fncountcount(xs : List(a)) : Int#

Returns the number of elements in the list. O(n).

Prefer this over computing length inside a recursive helper when possible.

Examples

march> Enum.count([1, 2, 3])
3
march> Enum.count([])
0
fndedupdedup(xs : List(a)) : List(a)#

Remove consecutive duplicate elements (structural equality). O(n).

Only adjacent duplicates are removed. For all-duplicates removal, use uniq.

Examples

march> Enum.dedup([1, 1, 2, 3, 3, 2])
[1, 2, 3, 2]
march> Enum.dedup([])
[]
fndrop_whiledrop_while(xs : List(a), pred : a -> Bool) : List(a)#

Drop elements from the front while pred returns true, return the rest. O(n).

Examples

march> Enum.drop_while([1, 2, 3, 1], fn x -> x < 3)
[3, 1]
march> Enum.drop_while([1, 2], fn x -> x < 3)
[]
fneacheach(xs : List(a), f : a -> b) : Unit#

Applies f to each element for side effects and returns Unit. O(n).

Use when you want to perform an action (e.g. println) for each element and don't care about the result. Returns Unit regardless of f's return type.

Enum.each(["a", "b"], fn s -> println(s))
fnfilterfilter(xs : List(a), pred : a -> Bool[p]) : List(a[p])#

Returns only the elements for which pred returns true. O(n).

Preserves order and is stable (equal elements stay in original relative order). Every element of the result is known to satisfy pred (an abstract refinement p, as for List.filter).

Examples

march> Enum.filter([1, 2, 3, 4], fn x -> x > 2)
[3, 4]
fnfindfind(xs : List(a), pred : a -> Bool[p]) : Option(a[p])#

Returns the first element satisfying pred, or None if none does. O(n).

Use find when you need one element that matches a condition. If you need all matching elements, use filter instead. The element, when present, is known to satisfy pred (an abstract refinement p, as for List.filter).

Examples

march> Enum.find([1, 2, 3], fn x -> x > 1)
Some(2)
march> Enum.find([1, 2, 3], fn x -> x > 5)
None
fnflat_mapflat_map(xs : List(a), f : a -> List(b)) : List(b)#

Applies f to each element (f returns a list) and concatenates all results. Each step appends onto the accumulated output, so the cost is quadratic in the total output length (O(k^2) for k output elements); Iterable.flat_map is linear.

Use when each element expands to zero or more output elements (e.g. tokenizing, expanding ranges). Equivalent to Haskell's concatMap.

Examples

march> Enum.flat_map([1, 2, 3], fn x -> [x, x * 10])
[1, 10, 2, 20, 3, 30]
march> Enum.flat_map([1, 2], fn _ -> [])
[]
fnfoldfold(xs : List(a), zero : b, f : b -> a -> b) : b#

Left fold over a list. Argument order: collection first, init second, callback last (uncurried-collection convention). The callback f is curried as f(acc)(x) = new_acc. O(n).

This is the primitive from which most other Enum functions can be derived. Use fold when you need to accumulate a single value from all elements. Returns zero for an empty list.

Examples

march> Enum.fold([1, 2, 3], 0, fn acc -> fn x -> acc + x)
6
march> Enum.fold(["a", "b"], "", fn acc -> fn x -> acc ++ x)
"ab"
fnfrequenciesfrequencies(xs : List(a)) : List((a, Int))#

Count occurrences of each element. Returns List((element, count)) in order of first appearance. O(n) average using a hash map for counting.

Examples

march> Enum.frequencies(["a", "b", "a", "c", "b", "a"])
[("a", 3), ("b", 2), ("c", 1)]
fnfrequencies_ordfrequencies_ord(xs : List(a), cmp : a -> a -> Bool) : List((a, Int))#

Count occurrences of each element, counting in a Map keyed with cmp. cmp : a -> a -> Bool where cmp(a)(b) = true means a < b. Output order is the Map's hash-traversal order: unspecified, neither sorted nor first-appearance. Use frequencies when order matters.

Prefer this over frequencies for large lists of Int, String, or any orderable type.

Examples

march> Enum.find(Enum.frequencies_ord(["a", "b", "a", "c"], fn a -> fn b -> a < b), fn p -> Tuple.first(p) == "a")
Some(("a", 2))
fngroup_bygroup_by(xs : List(a), key : a -> k) : List((k, List(a)))#

Groups consecutive elements with the same key into (key, [elements]) pairs. O(n).

Note: only CONSECUTIVE equal-key elements are grouped. If the same key appears non-consecutively, it produces multiple groups (unlike a hash-group-by). This matches Elixir's chunk_by semantics, not group_by.

Design: consecutive-only grouping avoids the need for a map/hashtable, keeping this function pure and O(n) without sorting. Keys are compared with ==. Returns [] for an empty list.

Examples

march> Enum.group_by([1, 1, 2, 3, 3, 1], fn x -> x)
[(1, [1, 1]), (2, [2]), (3, [3, 3]), (1, [1])]
fnintersperseintersperse(xs : List(a), sep : a) : List(a)#

Insert sep between every adjacent pair of elements. O(n).

Examples

march> Enum.intersperse([1, 2, 3], 0)
[1, 0, 2, 0, 3]
march> Enum.intersperse([1], 0)
[1]
march> Enum.intersperse([], 0)
[]
fnintrosort_byintrosort_by(xs : List(a), cmp : a -> a -> Bool) : List(a)#

Sorts xs using Introsort. Unstable, O(n log n) worst-case guaranteed.

Prefer over timsort_by when:

  • worst-case O(n log n) is required (security-sensitive or adversarial input)
  • stability is not needed and you want predictable worst-case behavior
  • you are sorting data with no natural runs

Examples

march> Enum.introsort_by([3, 1, 2], fn a -> fn b -> a <= b)
[1, 2, 3]
fnmapmap(xs : List(a), f : a -> b) : List(b)#

Applies f to each element and returns a new list of results. O(n).

This is the primary way to transform a collection element-by-element.

Examples

march> Enum.map([1, 2, 3], fn x -> x * 2)
[2, 4, 6]
fnmax_bymax_by(xs : List(a), f : a -> k) : Option(a)#

Return Some(element) with the maximum f(element) value, or None for empty. Uses > for key comparison, so on ties the first such element wins. O(n).

Examples

march> Enum.max_by(["apple", "fig", "banana"], fn s -> string_length(s))
Some("banana")
march> Enum.max_by(["ab", "cd"], fn s -> string_length(s))
Some("ab")
fnmin_bymin_by(xs : List(a), f : a -> k) : Option(a)#

Return Some(element) with the minimum f(element) value, or None for empty. Uses < for key comparison, so on ties the first such element wins. O(n).

Examples

march> Enum.min_by(["banana", "fig", "apple"], fn s -> string_length(s))
Some("fig")
march> Enum.min_by([], fn s -> string_length(s))
None
fnproductproduct(xs : List(Int)) : Int#

Product of a list of integers. Returns 1 for empty list. O(n).

Examples

march> Enum.product([1, 2, 3, 4, 5])
120
march> Enum.product([])
1
fnreducereduce(xs : List(a), f : a -> a -> a) : Option(a)#

Like fold, but uses the first element as the initial accumulator. Returns None for empty lists, Some(result) otherwise. O(n). f is curried: f(acc)(x) returns the new accumulator.

Prefer over fold when there is no natural zero value (e.g. max, min, string concatenation without a starting value).

Examples

march> Enum.reduce([3, 7, 2], fn a -> fn b -> if a > b do a else b end)
Some(7)
march> Enum.reduce([], fn a -> fn b -> a + b)
None
fnscanscan(xs : List(a), zero : b, f : b -> a -> b) : List(b)#

Like fold but returns a list of all intermediate accumulator values, including the initial value. O(n).

The returned list always has length len(xs) + 1. f is curried: f(acc)(x) returns the next accumulator.

Examples

march> Enum.scan([1, 2, 3, 4], 0, fn acc -> fn x -> acc + x)
[0, 1, 3, 6, 10]
march> Enum.scan([], 0, fn acc -> fn x -> acc + x)
[0]
fnslideslide(xs : List(a), n : Int) : List(List(a))#

Return all overlapping windows of length n. O(n * m) where m is window size. Returns [] when the list is shorter than the window or n is negative; n == 0 yields one empty window per element.

Examples

march> Enum.slide([1, 2, 3, 4], 3)
[[1, 2, 3], [2, 3, 4]]
march> Enum.slide([1, 2], 3)
[]
fnsort_bysort_by(xs : List(a), cmp : a -> a -> Bool) : List(a)#

Sorts xs by cmp, delegating to Sort.sort: comparison networks / mergesort below 16 elements, insertion sort below 64, Timsort from 64 up. O(n log n) except the 16..63 insertion-sort band (O(n^2), bounded).

Not guaranteed stable: the size-8 networks can reorder equal elements, and insertion sort keeps equal elements in order only for a strict < cmp while the merges do so only for a non-strict <= cmp. If equal elements must keep their input order, make the comparator total (e.g. break ties on an index from with_index).

cmp is curried: cmp(x)(y) = true means x should come before y.

Examples

march> Enum.sort_by([3, 1, 2], fn a -> fn b -> a <= b)
[1, 2, 3]
march> Enum.sort_by([3, 1, 2], fn a -> fn b -> a >= b)
[3, 2, 1]
fnsort_small_bysort_small_by(xs : List(a), cmp : a -> a -> Bool) : List(a)#

Sorts a small list (n <= 8) using optimal comparison networks. Falls back to mergesort for n > 8. Not stable: the networks can reorder equal elements.

Use sort_small_by when:

  • you know the list has at most 8 elements (e.g. sorting tuples, fixed fields)
  • you want provably minimal comparator count for the size

For general sorting, prefer sort_by.

Examples

march> Enum.sort_small_by([3, 1, 2], fn a -> fn b -> a <= b)
[1, 2, 3]
fnsumsum(xs : List(Int)) : Int#

Sum a list of integers. Returns 0 for empty list. O(n).

Examples

march> Enum.sum([1, 2, 3, 4, 5])
15
march> Enum.sum([])
0
fntake_whiletake_while(xs : List(a), pred : a -> Bool[p]) : List(a[p])#

Return the longest prefix for which pred returns true. O(n). Every element of the result is known to satisfy pred (an abstract refinement p, as for List.filter).

Examples

march> Enum.take_while([1, 2, 3, 1], fn x -> x < 3)
[1, 2]
fntimsort_bytimsort_by(xs : List(a), cmp : a -> a -> Bool) : List(a)#

Sorts xs using Timsort (Sort.timsort_by). O(n log n) worst-case, O(n) on sorted input.

Explicitly names the algorithm for documentation clarity. Prefer sort_by for application code; use timsort_by when you want near-linear behavior on natural runs (e.g. merging pre-sorted logs). Despite the algorithm's reputation it is not guaranteed stable here (see Sort.timsort_by).

Examples

march> Enum.timsort_by([3, 1, 2], fn a -> fn b -> a <= b)
[1, 2, 3]
fnuniquniq(xs : List(a)) : List(a)#

Remove all duplicate elements, keeping the first occurrence. O(n) average using a hash map for membership. For sorted output, prefer uniq_ord.

Examples

march> Enum.uniq([1, 2, 1, 3, 2])
[1, 2, 3]
fnuniq_byuniq_by(xs : List(a), f : a -> k) : List(a)#

Remove duplicates by key function, keeping the first occurrence. O(n) average using a hash map for membership. For large lists where the key type is orderable, uniq_ord with a key-derived comparator also works.

Examples

Keeps the first string with each initial letter:

march> Enum.uniq_by(["alice", "bob", "anna"], fn s -> string_slice(s, 0, 1))
["alice", "bob"]
fnuniq_orduniq_ord(xs, cmp)#

Remove duplicates from an ordered list in O(n log n), keeping the first occurrence of each value. Requires a less-than comparator matching Set's convention: cmp : a -> a -> Bool where cmp(a)(b) == true means a < b. Output preserves insertion order.

Prefer this over uniq when your element type has a natural ordering (Int, String, etc.) and the list is large enough that O(n²) is painful.

Examples

march> Enum.uniq_ord([3, 1, 2, 1, 3], fn a -> fn b -> a < b)
[3, 1, 2]
fnwith_indexwith_index(xs : List(a)) : List((a, Int))#

Pair each element with its zero-based index. O(n).

Note the pair order is (element, index), the reverse of Iterable.enumerate.

Examples

march> Enum.with_index([10, 20, 30])
[(10, 0), (20, 1), (30, 2)]
fnzipzip(xs : List(a), ys : List(b)) : List((a, b))#

Zip two lists into a list of pairs, stopping at the shorter list. O(min(|a|,|b|)).

Examples

march> Enum.zip([1, 2, 3], ["a", "b"])
[(1, "a"), (2, "b")]
fnzip_withzip_with(xs : List(a), ys : List(b), f : a -> b -> c) : List(c)#

Zips two lists together applying f to corresponding elements. Stops at the shorter list. O(min(|xs|, |ys|)). f is curried: f(x)(y) returns the combined value.

Use zip_with when you want to combine two collections element-by-element. Equivalent to Haskell's zipWith.

Examples

march> Enum.zip_with([1, 2, 3], [10, 20], fn x -> fn y -> x + y)
[11, 22]