Iterable.ts overview
Lazy Iterable-returning counterparts of select MArray functions: predicates, indexed search, indexed (un)grouping, sorted-iterator merging/difference, and cycle-aware unfolding.
Mental model
- Every function accepts an
Iterable<A>and returns a newIterable<B>: nothing is computed until the result is iterated (e.g. spread with[...result]or consumed by afor...of). - Each function has an eager
MArraytwin of the same name, with identical semantics — only eagerness differs. - All functions are curried, data-last — call as
pipe(self, MIterable.fn(arg)). - Equality-based functions (e.g.
longestCommonSubArray,differenceSorted) compare withEqual.equals.
Common tasks
- Search:
findAll,longestCommonSubArray - Drop:
takeRightBut - Modify by position:
modifyHead,modifyTail - Ungroup:
ungroup - Sorted operations:
mergeSorted,differenceSorted - Construct:
unfold
Gotchas
- An Iterable returned by any function here can only be consumed once per source Iterable if that source is itself single-pass (e.g. a generator); re-iterating the result then re-reads the source from where it left off, not from the start. Sourcing from an
Array(multi-pass) avoids this.
Quickstart
Example (Filtering lazily, materializing only at the end)
import { pipe } from "effect"
import * as MIterable from "@parischap/effect-lib/MIterable"
console.log([
...pipe(
[1, 3, 2, 4, 3],
MIterable.findAll((n) => n === 3)
)
]) // [1, 4]
See
MArray— eager counterparts of these functions
Exports Grouped by Category
Constructors
unfold
Curried version of Iterable.unfold with optional cycle detection.
- Without
seedEquivalence, keep generating untilfreturnsOption.none. - With
seedEquivalence, every seed is recorded as it is produced; if the same seed reappears,cycleSourceis asomeof theAproduced the first time that seed was processed, allowing the caller to break the cycle. - Nothing is computed until the result is iterated:
fruns once per element actually pulled.
Example (Unfold without cycle detection)
import { Option, pipe } from "effect"
import * as MIterable from "@parischap/effect-lib/MIterable"
console.log([
...pipe(
0,
MIterable.unfold((n) => (n < 3 ? Option.some([n, n + 1] as const) : Option.none()))
)
]) // [0, 1, 2]
See
MArray.unfold— eager counterpart
Signature
declare const unfold: {
<S, A>(f: (s: S) => Option.Option<MTypes.Pair<A, S>>): (s: S) => Iterable<A>
<S, A>(
f: (s: S, cycleSource: Option.Option<NoInfer<A>>) => Option.Option<MTypes.Pair<A, S>>,
seedEquivalence: Equivalence.Equivalence<S>
): (s: S) => Iterable<A>
}
Utils
differenceSorted
Returns the elements of self not present in that. Element equality is Equal.equals.
- Use to compute a sorted set difference in a single lazy linear pass.
- Both inputs must already be sorted according to
o; otherwise the result is undefined. - Reads one element ahead from each input at a time; never materializes either input fully.
Example (Sorted difference)
import { Order, pipe } from "effect"
import * as MIterable from "@parischap/effect-lib/MIterable"
console.log([...pipe([1, 2, 3, 4, 5], MIterable.differenceSorted(Order.Number)([2, 4]))]) // [1, 3, 5]
See
MArray.differenceSorted— eager counterpart
Signature
declare const differenceSorted: <A>(o: Order.Order<A>) => (that: Iterable<A>) => (self: Iterable<A>) => Iterable<A>
findAll
Returns the indexes of all elements of self satisfying predicate, in ascending order.
- Use to locate every position matching a condition.
- Yields no index when no element matches.
- Consumes
selflazily, one element at a time.
Example (Indexes of matching elements)
import { pipe } from "effect"
import * as MIterable from "@parischap/effect-lib/MIterable"
console.log([
...pipe(
[1, 3, 2, 4, 3],
MIterable.findAll((n) => n === 3)
)
]) // [1, 4]
See
MArray.findAll— eager counterpart
Signature
declare const findAll: <A>(predicate: Predicate.Predicate<NoInfer<A>>) => (self: Iterable<A>) => Iterable<number>
longestCommonSubArray
Returns the longest common prefix of self and that, comparing elements with Equal.equals.
- Use to find the leading elements two iterables agree on.
- Yields no element when the first elements already differ.
- Stops reading
selfandthatas soon as a mismatch is found.
Example (Longest common prefix)
import { pipe } from "effect"
import * as MIterable from "@parischap/effect-lib/MIterable"
console.log([...pipe([1, 2, 4], MIterable.longestCommonSubArray([1, 2, 3]))]) // [1, 2]
See
MArray.longestCommonSubArray— eager counterpart
Signature
declare const longestCommonSubArray: <A>(that: Iterable<A>) => (self: Iterable<A>) => Iterable<A>
mergeSorted
Stably merges two iterables already sorted according to o.
- Both inputs must already be sorted by
o. - The merge is stable: equal elements from
selfprecede equal elements fromthat. - Reads one element ahead from each input at a time; never materializes either input fully.
Example (Merge sorted iterables)
import { Order, pipe } from "effect"
import * as MIterable from "@parischap/effect-lib/MIterable"
console.log([...pipe([1, 3, 5], MIterable.mergeSorted(Order.Number)([2, 4, 6]))]) // [1, 2, 3, 4, 5, 6]
See
MArray.mergeSorted— eager counterpart
Signature
declare const mergeSorted: <A>(o: Order.Order<A>) => (that: Iterable<A>) => (self: Iterable<A>) => Iterable<A>
modifyHead
Returns a copy of self with the first element transformed by f.
- Use to update only the leading element.
- Yields no element when
selfis empty.
Example (Modify first element)
import { pipe } from "effect"
import * as MIterable from "@parischap/effect-lib/MIterable"
console.log([
...pipe(
[1, 2, 3],
MIterable.modifyHead((x) => x * 2)
)
]) // [2, 2, 3]
See
MArray.modifyHead— eager counterpart
Signature
declare const modifyHead: <A, B>(f: (a: NoInfer<A>) => B) => (self: Iterable<A>) => Iterable<A | B>
modifyTail
Returns a copy of self with every element except the first transformed by f.
- Use to apply a transformation to all elements but the leading one.
- Yields
selfunchanged when it has zero or one elements.
Example (Modify all but the first)
import { pipe } from "effect"
import * as MIterable from "@parischap/effect-lib/MIterable"
console.log([
...pipe(
[1, 2, 3, 4],
MIterable.modifyTail((x) => x * 2)
)
]) // [1, 4, 6, 8]
See
MArray.modifyTail— eager counterpart
Signature
declare const modifyTail: <A, B>(f: (a: NoInfer<A>, i: number) => B) => (self: Iterable<A>) => Iterable<A | B>
takeRightBut
Returns all elements of self except the first n.
- Use to drop a prefix of fixed size.
- When
n >= size of self, yields no element. nshould be a non-negative integer.
Example (Drop leading elements)
import { pipe } from "effect"
import * as MIterable from "@parischap/effect-lib/MIterable"
console.log([...pipe([1, 2, 3, 4], MIterable.takeRightBut(2))]) // [3, 4]
See
MArray.takeRightBut— eager counterpart
Signature
declare const takeRightBut: (n: number) => <A>(self: Iterable<A>) => Iterable<A>
ungroup
Flattens a two-dimensional iterable, tagging each element with the index of its source row.
- Use as the lazy companion to
MArray.groupByNum. - Order is preserved: rows are visited in order, and each row is flattened left to right.
Example (Tagged flatten)
import * as MIterable from "@parischap/effect-lib/MIterable"
console.log([
...MIterable.ungroup([
[1, 2, 3],
[4, 5]
])
]) // [[0, 1], [0, 2], [0, 3], [1, 4], [1, 5]]
See
MArray.ungroup— eager counterpart
Signature
declare const ungroup: <A>(as: Iterable<Iterable<A>>) => Iterable<[number, A]>