Lazy Iterators
Tova provides lazy iterators through the iter() function and the Seq class. Unlike eager array operations that create intermediate arrays at each step, lazy iterators process elements one at a time through the entire pipeline, only computing values when needed.
Creating Iterators
iter
iter(iterable) -> SeqWraps any iterable (array, string, range, generator) in a lazy Seq:
seq = iter([1, 2, 3, 4, 5])
seq = iter("hello")
seq = iter(range(1000000))Seq Methods
filter
seq.filter(fn) -> SeqLazily filters elements, keeping only those where fn returns true.
evens = iter(range(100)).filter(fn(x) x % 2 == 0)map
seq.map(fn) -> SeqLazily transforms each element.
doubled = iter([1, 2, 3]).map(fn(x) x * 2)take
seq.take(n) -> SeqTakes only the first n elements.
first_ten = iter(range(1000000)).take(10)drop
seq.drop(n) -> SeqSkips the first n elements.
rest = iter([1, 2, 3, 4, 5]).drop(2)
// 3, 4, 5zip
seq.zip(other) -> SeqPairs elements from two sequences. Stops at the shorter one.
pairs = iter([1, 2, 3]).zip(iter(["a", "b", "c"]))
// (1, "a"), (2, "b"), (3, "c")flat_map
seq.flat_map(fn) -> SeqMaps each element to a sequence and flattens the results.
iter(["hello world", "foo bar"])
.flat_map(fn(s) iter(split(s, " ")))
// "hello", "world", "foo", "bar"enumerate
seq.enumerate() -> SeqPairs each element with its index.
iter(["a", "b", "c"]).enumerate()
// (0, "a"), (1, "b"), (2, "c")Consuming Methods
These methods trigger evaluation of the lazy pipeline and produce a final value.
collect / toArray
seq.collect() -> [T]
seq.toArray() -> [T]Evaluates the pipeline and collects results into an array.
result = iter(range(100))
.filter(fn(x) x % 2 == 0)
.map(fn(x) x * x)
.take(5)
.collect()
// [0, 4, 16, 36, 64]reduce
seq.reduce(fn, init) -> TReduces the sequence to a single value.
total = iter([1, 2, 3, 4]).reduce(fn(acc, x) acc + x, 0)
// 10first
seq.first() -> T | NilReturns the first element, or nil if empty.
iter([10, 20, 30]).first() // 10
iter([]).first() // nilcount
seq.count() -> IntCounts the number of elements.
iter(range(100)).filter(fn(x) x % 7 == 0).count()
// 15forEach
seq.forEach(fn) -> NilExecutes a function for each element (for side effects).
iter(["Alice", "Bob"]).forEach(fn(name) print("Hello, {name}!"))any
seq.any(fn) -> BoolReturns true if any element satisfies the predicate. Short-circuits on first match.
iter([1, 2, 3]).any(fn(x) x > 2) // trueall
seq.all(fn) -> BoolReturns true if all elements satisfy the predicate. Short-circuits on first failure.
iter([2, 4, 6]).all(fn(x) x % 2 == 0) // truefind
seq.find(fn) -> T | NilReturns the first element matching the predicate, or nil.
iter(users).find(fn(u) u.name == "Alice")Pipe Integration
Lazy iterators work naturally with the pipe operator:
result = range(1000000)
|> iter()
|> .filter(fn(x) x % 2 == 0)
|> .map(fn(x) x * x)
|> .take(10)
|> .collect()Lazy vs Eager
| Eager (array functions) | Lazy (Seq) | |
|---|---|---|
| Intermediate arrays | Created at each step | None |
| Memory | Proportional to data size | Constant |
| Short-circuit | Processes all elements | Stops when enough collected |
| Best for | Small/medium collections | Large data, early termination |
// Eager: creates 3 intermediate arrays
result = range(1000000)
|> filter(fn(x) x % 2 == 0)
|> map(fn(x) x * x)
|> take(5)
// Lazy: no intermediate arrays, stops after 5 results
result = iter(range(1000000))
.filter(fn(x) x % 2 == 0)
.map(fn(x) x * x)
.take(5)
.collect()