Skip to main content Link Search Menu Expand Document (external link)

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 new Iterable<B>: nothing is computed until the result is iterated (e.g. spread with [...result] or consumed by a for...of).
  • Each function has an eager MArray twin 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 with Equal.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 until f returns Option.none.
  • With seedEquivalence, every seed is recorded as it is produced; if the same seed reappears, cycleSource is a some of the A produced the first time that seed was processed, allowing the caller to break the cycle.
  • Nothing is computed until the result is iterated: f runs 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>
}

Source

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>

Source

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 self lazily, 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>

Source

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 self and that as 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>

Source

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 self precede equal elements from that.
  • 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>

Source

modifyHead

Returns a copy of self with the first element transformed by f.

  • Use to update only the leading element.
  • Yields no element when self is 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>

Source

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 self unchanged 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>

Source

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.
  • n should 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>

Source

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]>

Source