Skip to content

std/iter

Lazy iteration over any Iter<T>.

The lazy adapters (map, filter, take, flat_map, …) are type functions on IterIter.map(xs, f) — not protocol requirements. Each returns Iter<...> backed by a private Seq — a source expressed as nothing but its own push loop — so chains stay lazy, consuming one element at a time through the whole pipeline without building intermediate lists. There are no eager, container-preserving adapter functions on the collections: to get a container back, append an explicit materialize step — Iter.map(xs, f) |> Iter.to_list(), Iter.filter(s, p) |> Iter.to_set().

The terminals are also type functions on IterIter.reduce(xs, f) — over any Iter<T>. The Iter interface itself is just the { each_while, known_count } protocol. Terminals come in two kinds:

  • Streaming: reduce, each, find, any?, all?, empty? produce a scalar; no concrete collection is ever built.
  • Materializing: to_list/to_set/sort, plus to_map/to_string, consume the iterator and produce a concrete collection.

A collection’s own form of an op may diverge from the generic one (String.reverse → String rebuilds a String, not the generic List), so those collections keep a container-specific native function; the generic form (Iter.reverse -> List) stays an Iter type function.

Infinite sequences: Iter.from(0) / Iter.iterate(seed, step) / Iter.repeat(x) / Iter.cycle(xs) produce iterators that don’t terminate on their own. Bound them with Iter.take(n) or Iter.take_while(pred) before a materializing terminal, or use short-circuiting terminals like Iter.find / Iter.any?.

If you want a List back from a List, the spelling is always the explicit Iter.map(xs, f) |> Iter.to_list() — the lazy adapter plus a materialize step. There is no eager List.map; the materialize step is what makes the result strict.

Iterables in Nomi are replayable: binding x = Iter.map(xs, f) and consuming x multiple times yields consistent results, because a source walks itself from a value it never mutates.

Iter also hosts loop, the state-threading repetition primitive — it takes no collection, but its callback speaks the same break/continue protocol as every other iteration callback here.

Import with import std/iter.

Interfaces

interface Iter<T> {
    fn each_while(collection: self, yield: (T) -> Bool): Bool
    fn known_count(collection: self): Maybe<Int>
}

Protocol for pushing elements to a consumer.

A type implementing Iter<T> provides a single function, each_while, that walks its own elements and hands each one to yield. Returning False from yield asks the source to stop; each_while itself returns True when the source ran to exhaustion and False when a consumer stopped it early. The source drives its own loop, so no self appears anywhere in the signature and nothing is rebuilt per element. This is Go’s iter.Seq shape, spelled for Nomi.

Because each_while reads a collection it never mutates, Nomi iterators are replayable — driving the same iterator binding twice yields the same sequence.

Built-in types like List, Map, String, and ranges over Discrete values (Int, Codepoint) already implement Iter, so the operations below work directly on them. To implement it for a custom type, walk your own structure and stop as soon as yield says to:

pub struct Countdown {
  from: Int
}

impl Iter for Countdown {
  fn each_while(c: Countdown, yield: (Int) -> Bool): Bool {
    if c.from <= 0 {
      True
    } else {
      if yield(c.from) {
        each_while(Countdown{from: c.from - 1}, yield)
      } else {
        False
      }
    }
  }
}
fn each_while(collection: self, yield: (T) -> Bool): Bool

interface Iter

fn known_count(collection: self): Maybe<Int>

interface Iter

fn loop<S>(f: (S) -> S): S

type function on Iter

Loops indefinitely, invoking f each iteration. The callback either carries state (declare its first parameter with a default — that default is the initial state, e.g. |n = 0|) or is stateless (|| body). The callback’s return value becomes the next iteration’s state. The state type, the break value type, and the loop’s result type are all one type S: for a stated callback S comes from the default; for a stateless callback S is inferred from the break value.

Use break value to exit with value, or bare break to exit with Unit. Without an explicit break this loops forever.

Interactive Tests

assert Iter.loop(|n = 0|
  if n >= 3 {
    break n
  } else {
    n + 1
  }
) == 3
assert Iter.loop(|| { break 42 }) == 42
fn reduce<T, U>(source: Iter<T>, f: (U, T) -> U): U

type function on Iter

Reduces a collection to a single value by folding.

The initial accumulator is provided by the lambda’s default parameter: If the lambda has no default (e.g., |a, b| ...), the first element of the collection is used as the initial accumulator and iteration starts at the second element. Errors if the collection is empty.

Supports break / break value / continue in the callback.

Interactive Tests

assert Iter.reduce([1, 2, 3], |acc = 0, x| acc + x) == 6
fn find<T>(source: Iter<T>, f: (T) -> Bool): Maybe<T>

type function on Iter

Returns the first element matching the predicate, or None.

Interactive Tests

assert Iter.find([1, 2, 3, 4], |x| x > 2) == Some(3)
assert Iter.find([1, 2], |x| x > 99) == None
fn sort_with<T>(source: Iter<T>, compare: (T, T) -> Ordering): List<T>

type function on Iter

Consumes the iterator and returns its elements as a List sorted by the comparator. A materializing terminal — like to_list, but ordered — so it works on any iterable (a lazy chain, a range, a List). The sort is stable: equal elements keep their input order.

This is the explicit-comparator sort primitive (no Comparable bound). When the element type is Comparable, prefer sort (natural order) or sort_by (key projection), which call this under the hood.

Interactive Tests

assert Iter.sort_with([3, 1, 2], Int.compare) == [1, 2, 3]
fn sort<T>(source: Iter<T>, direction: Direction): List<T> where T: Comparable

type function on Iter

Consumes the iterator and returns its elements as a List in natural order, using the element type’s Comparable impl. Defaults to ascending; pass Descending to flip. A materializing terminal — works on any iterable (a lazy chain, a Range, a List). The sort is stable: equal elements keep their input order.

Interactive Tests

assert Iter.sort([3, 1, 2]) == [1, 2, 3]
assert Iter.sort([3, 1, 2], Direction.Descending) == [3, 2, 1]
fn sort_by<T, K>(source: Iter<T>, direction: Direction, key: (T) -> K): List<T> where K: Comparable

type function on Iter

Sorts by a Comparable key projected from each element, returning a List in key order. Defaults to ascending; pass Descending to flip. A materializing terminal; the sort is stable. The trailing key lambda may be passed with direction omitted (Iter.sort_by(xs, |x| x.k)); the default fills the skipped slot.

Interactive Tests

assert Iter.sort_by(["bb", "a", "ccc"], |s| String.length(s)) == [
  "a",
  "bb",
  "ccc",
]
assert Iter.sort_by([1, 2, 3], Direction.Descending, |n| n) == [3, 2, 1]
fn each<T>(source: Iter<T>, f: (T) -> Unit): Unit

type function on Iter

Calls f for each element. Returns Unit (discards the accumulator).

Interactive Tests

seen = Iter.reduce([1, 2, 3], |acc = 0, n| acc + n)
assert seen == 6
assert Iter.each([1, 2, 3], |_n| Unit) == Unit
fn any?<T>(source: Iter<T>, f: (T) -> Bool): Bool

type function on Iter

Returns true if any element matches the predicate. Short-circuits on the first match.

Interactive Tests

assert Iter.any?([1, 2, 3], |x| x > 2)
refute Iter.any?([1, 2], |x| x > 99)
fn all?<T>(source: Iter<T>, f: (T) -> Bool): Bool

type function on Iter

Returns true if all elements match the predicate. Vacuously true for an empty iterator. Short-circuits on the first miss.

Interactive Tests

assert Iter.all?([2, 4, 6], |x| x > 0)
refute Iter.all?([2, -4, 6], |x| x > 0)
fn empty?<T>(source: Iter<T>): Bool

type function on Iter

Returns true if the iterator is empty. Short-circuits on the first element.

Interactive Tests

assert Iter.empty?([])
refute Iter.empty?([1])
fn not_empty?<T>(source: Iter<T>): Bool

type function on Iter

Returns true if the iterator has at least one element. Short-circuits.

Interactive Tests

assert Iter.not_empty?([1])
refute Iter.not_empty?([])
fn first<T>(source: Iter<T>): Maybe<T>

type function on Iter

Returns the first element, or None if the iterator is empty. Terminal; short-circuits after one step. (List spells this head, paired with tail — the cons-list idiom; first is the stream-vocabulary name that every iterator shares.)

Interactive Tests

assert Iter.first([1, 2, 3]) == Some(1)
assert Iter.first([]) == None
fn last<T>(source: Iter<T>): Maybe<T>

type function on Iter

Returns the last element, or None if the iterator is empty. Terminal; consumes the whole iterator (does not terminate on an infinite source).

Interactive Tests

assert Iter.last([1, 2, 3]) == Some(3)
assert Iter.last([]) == None
fn at<T>(source: Iter<T>, index: Int): Maybe<T>

type function on Iter

Returns the element at the given zero-based index, or None if the index is out of range (or negative). Walks the iterator, so it’s O(index).

Interactive Tests

assert Iter.at([10, 20, 30], 1) == Some(20)
assert Iter.at([10, 20, 30], 9) == None
assert Iter.at([10, 20, 30], -1) == None
fn partition<T>(source: Iter<T>, f: (T) -> Bool): (List<T>, List<T>)

type function on Iter

Splits the elements into (matches, rest) by a predicate, preserving the original order within each. Materializing.

Interactive Tests

assert Iter.partition([1, 2, 3, 4], |x| x > 2) == ([3, 4], [1, 2])
fn group_by<T, K>(source: Iter<T>, key_fn: (T) -> K): Map<K, List<T>>

type function on Iter

Groups elements by a key function. Each group preserves the original order of its members. Materializing.

Interactive Tests

assert Iter.group_by([1, 2, 3, 4], |x| x % 2) == {1 => [1, 3], 0 => [2, 4]}
fn to_list<T>(source: Iter<T>): List<T>

type function on Iter

Consumes the iterator and materializes its elements into a List. A List argument is returned as-is (O(1) — lists are immutable); any other iterator is folded into a fresh List.

Interactive Tests

assert Iter.from(0) |> Iter.take(3) |> Iter.to_list() == [0, 1, 2]
fn to_set<T>(source: Iter<T>): Set<T>

type function on Iter

Consumes the iterator and materializes its elements into a Set, discarding duplicates.

Interactive Tests

assert Iter.to_set([1, 2, 2, 3]) == #{1, 2, 3}
fn with_index<T>(source: Iter<T>): Iter<(Int, T)>

type function on Iter

Lazy: pairs each element of source with a zero-based index.

Interactive Tests

assert ["a", "b", "c"] |> Iter.with_index() |> Iter.to_list() == [
  (0, "a"),
  (1, "b"),
  (2, "c"),
]
fn take<T>(source: Iter<T>, n: Int): Iter<T>

type function on Iter

Lazy: yields at most the first n elements of source. Useful for bounding an infinite iterator.

Interactive Tests

assert Iter.from(0) |> Iter.take(3) |> Iter.to_list() == [0, 1, 2]
fn cycle<T>(source: Iter<T>): Iter<T>

type function on Iter

Lazy: cycles through source endlessly. Empty source yields nothing.

Interactive Tests

assert [1, 2, 3]
|> Iter.cycle()
|> Iter.take(7)
|> Iter.to_list() == [1, 2, 3, 1, 2, 3, 1]
fn concat<T>(a: Iter<T>, b: Iter<T>): Iter<T>

type function on Iter

Lazy: yields all elements of a, then all of b.

Interactive Tests

assert Iter.concat([1, 2], [3, 4]) |> Iter.to_list() == [1, 2, 3, 4]
fn zip<T, U>(a: Iter<T>, b: Iter<U>): Iter<(T, U)>

type function on Iter

Lazy: pairs elements from two iterators. Stops when either exhausts.

Interactive Tests

assert Iter.zip([1, 2, 3], ["a", "b"]) |> Iter.to_list() == [(1, "a"), (2, "b")]
fn take_while<T>(source: Iter<T>, pred: (T) -> Bool): Iter<T>

type function on Iter

Lazy: yields elements of source while pred returns true. In the callback, value is the keep/stop decision: break True takes the current element then stops, break False/bare break stop without it, continue skips it.

Interactive Tests

assert Iter.from(1)
|> Iter.take_while(|x| x < 4)
|> Iter.to_list() == [1, 2, 3]
fn map<T, U>(source: Iter<T>, f: (T) -> U): Iter<U>

type function on Iter

Lazy: applies f to each element of source.

Interactive Tests

assert [1, 2, 3] |> Iter.map(|x| x * 10) |> Iter.to_list() == [10, 20, 30]
fn filter<T>(source: Iter<T>, pred: (T) -> Bool): Iter<T>

type function on Iter

Lazy: keeps only elements where pred returns true. In the callback, value is the keep/drop decision: break True keeps the current element then stops, break False drops it then stops, bare break stops without deciding, continue skips. Append |> Iter.to_list() for the strict, List-returning form.

Interactive Tests

assert [1, 2, 3, 4] |> Iter.filter(|x| x > 2) |> Iter.to_list() == [3, 4]
fn drop<T>(source: Iter<T>, n: Int): Iter<T>

type function on Iter

Lazy: skips the first n elements of source.

Interactive Tests

assert [1, 2, 3, 4] |> Iter.drop(2) |> Iter.to_list() == [3, 4]
fn drop_while<T>(source: Iter<T>, pred: (T) -> Bool): Iter<T>

type function on Iter

Lazy: drops the leading run of elements where pred is true.

Interactive Tests

assert [1, 2, 3, 1] |> Iter.drop_while(|x| x < 3) |> Iter.to_list() == [3, 1]
fn flat_map<T, U>(source: Iter<T>, f: (T) -> Iter<U>): Iter<U>

type function on Iter

Lazy: for each element of source, apply f and yield all its elements.

Interactive Tests

assert [1, 2, 3]
  |> Iter.flat_map(|x| [x, x * 10])
  |> Iter.to_list()
  |> List.equal?([1, 10, 2, 20, 3, 30])
fn chunks<T>(source: Iter<T>, size: Int): Iter<List<T>>

type function on Iter

Lazy: yields adjacent fixed-size chunks as Lists. The final chunk may be shorter. A non-positive size yields no chunks.

Interactive Tests

assert [1, 2, 3, 4, 5] |> Iter.chunks(2) |> Iter.to_list() == [
  [1, 2],
  [3, 4],
  [5],
]
fn chunk_by<T, K>(source: Iter<T>, key_fn: (T) -> K): Iter<List<T>> where K: Equatable

type function on Iter

Lazy: yields adjacent runs whose elements produce equal keys. Unlike group_by, this never reorders or merges non-adjacent elements.

Interactive Tests

assert [1, 1, 2, 2, 1] |> Iter.chunk_by(|x| x) |> Iter.to_list() == [
  [1, 1],
  [2, 2],
  [1],
]
assert ["ant", "ape", "bee", "bat", "cat"]
  |> Iter.chunk_by(|s| String.contains?(s, "a"))
  |> Iter.to_list()
  |> List.equal?([["ant", "ape"], ["bee"], ["bat", "cat"]])
fn from(start: Int): Iter<Int>

type function on Iter

Infinite iterator yielding start, start+1, start+2, …

Interactive Tests

assert Iter.from(10) |> Iter.take(3) |> Iter.to_list() == [10, 11, 12]
fn repeat<T>(x: T): Iter<T>

type function on Iter

Infinite iterator yielding x forever.

Interactive Tests

assert Iter.repeat("ha") |> Iter.take(3) |> Iter.to_list() == ["ha", "ha", "ha"]
fn iterate<T>(seed: T, step: (T) -> T): Iter<T>

type function on Iter

Infinite iterator: seed, step(seed), step(step(seed)), …

The step callback runs in iter-callback context:

  • Plain return value v: emit the current state, advance to v.
  • break v: emit v as the final element, then stop.
  • bare break: stop immediately without emitting.
  • continue: runtime error — iterate’s step is the state-advancement, so continue has no next state to fall forward to. Use Iter.iterate(...) |> Iter.filter(...) to skip states.

Interactive Tests

assert Iter.iterate(1, |x| x * 2)
|> Iter.take(4)
|> Iter.to_list() == [1, 2, 4, 8]
assert Iter.iterate(0, |n|
  if n > 5 {
    break n
  } else {
    n + 1
  }
)
  |> Iter.to_list()
  |> List.equal?([0, 1, 2, 3, 4, 5, 6])
fn to_map<K, V>(source: Iter<(K, V)>): Map<K, V>

type function on Iter

Consumes the iterator and materializes its (K, V) pair elements into a Map. Runtime error if elements aren’t pairs.

Stays a Go extern: it keeps the pair-element-type constraint error at the builtin boundary, and avoids iter reaching for std/maps.

Interactive Tests

assert Iter.to_map([("a", 1), ("b", 2)]) == {"a" => 1, "b" => 2}
fn to_string(source: Iter<String>): String

type function on Iter

Consumes the iterator and concatenates its string elements. Runtime error if elements aren’t strings.

Interactive Tests

assert Iter.to_string(["he", "llo"]) == "hello"
fn flatten<U>(source: Iter<Iter<U>>): List<U>

type function on Iter

Flattens an iterator of iterators into a single List, preserving order. Like to_map/to_string, its source has a specific element shape — the elements must themselves be iterable — so it’s a type function rather than a self-based Iter interface function.

Interactive Tests

assert Iter.flatten([[1, 2], [3], [4, 5]]) == [1, 2, 3, 4, 5]
fn reverse<T>(source: Iter<T>): List<T>

type function on Iter

Returns the elements of source reversed, as a List. A materializing terminal (consumes the whole iterator). Generic over any iterator. A collection that wants its own container back keeps a container-specific native function (String.reverse → String), which is why this generic form is a type function rather than an Iter protocol function: the two return types would otherwise have to diverge, and String.reverse could not equal Iter.reverse.

Interactive Tests

assert Iter.reverse([1, 2, 3]) == [3, 2, 1]
fn count<T>(source: Iter<T>): Int

type function on Iter

Returns the number of elements in source, consuming it. The single generic count over any Iter<T>: it consults the known_count protocol function first, so it’s O(1) on any source that stores its size (List/Map/Set/bounded ranges whose element type can count steps); everything else (lazy pipelines, String) inherits the None default and folds in O(n). Because the fold runs to exhaustion, Iter.count does not terminate on an infinite source — bound it with Iter.take(n) first. For the sized-only, non-consuming answer (None when unknown or unbounded), use Iter.known_count directly.

Interactive Tests

assert Iter.count([10, 20, 30]) == 3
assert Iter.from(0) |> Iter.take(5) |> Iter.count() == 5