// ets_tracing: off import "../Operator/index.js" import * as A from "../Collections/Immutable/Array/index.js" import * as Tp from "../Collections/Immutable/Tuple/index.js" import type { Either } from "../Either/index.js" import { identity } from "../Function/index.js" function* genOf(a: A) { yield a } function* genMap(iterator: Iterator, mapping: (a: A, i: number) => B) { let n = -1 while (true) { const result = iterator.next() if (result.done) { break } n += 1 yield mapping(result.value, n) } } function* genChain(iterator: Iterator, mapping: (a: A) => Iterable) { while (true) { const result = iterator.next() if (result.done) { break } const ib = mapping(result.value)[Symbol.iterator]() while (true) { const result = ib.next() if (result.done) { break } yield result.value } } } // inspired from "Closing Iterables is a Leaky Abstraction" by Reginald Braithwaite // https://raganwald.com/2017/07/22/closing-iterables-is-a-leaky-abstraction.html export function zipWith( iterableA: Iterable, iterableB: Iterable, zipper: (a: A, b: B) => C ): Iterable { return { [Symbol.iterator]() { let done = false const ia = iterableA[Symbol.iterator]() const ib = iterableB[Symbol.iterator]() return { next() { if (done) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return this.return!() } const va = ia.next() const vb = ib.next() return va.done || vb.done ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.return!() : { done: false, value: zipper(va.value, vb.value) } }, return(value?: unknown) { if (!done) { done = true if (typeof ia.return === "function") { ia.return() } if (typeof ib.return === "function") { ib.return() } } return { done: true, value } } } } } } export function map(f: (a: A, k: number) => B) { return (i: Iterable): Iterable => ({ [Symbol.iterator]: () => genMap(i[Symbol.iterator](), f) }) } export function map_(i: Iterable, f: (a: A, k: number) => B): Iterable { return { [Symbol.iterator]: () => genMap(i[Symbol.iterator](), f) } } export function zip(fb: Iterable) { return (fa: Iterable): Iterable> => zipWith(fa, fb, Tp.tuple) } export function zip_( fa: Iterable, fb: Iterable ): Iterable> { return zipWith(fa, fb, Tp.tuple) } export function chain(f: (a: A) => Iterable) { return (i: Iterable): Iterable => ({ [Symbol.iterator]: () => genChain(i[Symbol.iterator](), f) }) } export function chain_(i: Iterable, f: (a: A) => Iterable): Iterable { return { [Symbol.iterator]: () => genChain(i[Symbol.iterator](), f) } } export function ap(fa: Iterable) { return (fab: Iterable<(a: A) => B>): Iterable => chain_(fab, (f) => map_(fa, f)) } export function of(a: A): Iterable { return { [Symbol.iterator]: () => genOf(a) } } export function take_(a: Iterable, n: number): Iterable { return { *[Symbol.iterator]() { let i = 0 for (const x of a) { if (i++ >= n) { return } yield x } } } } export function skip_(a: Iterable, n: number): Iterable { return { *[Symbol.iterator]() { let i = 0 for (const x of a) { if (i++ >= n) { yield x } } } } } export const never: Iterable = { // eslint-disable-next-line @typescript-eslint/no-empty-function *[Symbol.iterator]() {} } export function foldMap(M: { empty: M; concat: (x: M, y: M) => M }) { return (f: (a: A, k: number) => M) => (fa: Iterable): M => { let res = M.empty let n = -1 const iterator = fa[Symbol.iterator]() // eslint-disable-next-line no-constant-condition while (true) { const result = iterator.next() if (result.done) { break } n += 1 res = M.concat(res, f(result.value, n)) } return res } } export function reduce(b: B, f: (b: B, a: A, i: number) => B) { return (fa: Iterable): B => reduce_(fa, b, f) } export function reduce_( fa: Iterable, b: B, f: (b: B, a: A, i: number) => B ): B { let res = b let n = -1 const iterator = fa[Symbol.iterator]() // eslint-disable-next-line no-constant-condition while (true) { const result = iterator.next() if (result.done) { break } n += 1 res = f(res, result.value, n) } return res } export function reduceRight(b: B, f: (a: A, b: B, i: number) => B) { return (fa: Iterable): B => { return A.reduceRightWithIndex_(Array.from(fa), b, (i, a, b) => f(a, b, i)) } } export function reduceRight_( fa: Iterable, b: B, f: (a: A, b: B, i: number) => B ): B { return A.reduceRightWithIndex_(Array.from(fa), b, (i, a, b) => f(a, b, i)) } export function concat(a: Iterable, b: Iterable): Iterable { return { *[Symbol.iterator]() { for (const x of a) { yield x } for (const x of b) { yield x } } } } export function flatten(a: Iterable>) { return chain_(a, identity) } export function partitionMap(f: (a: A) => Either) { return (as: Iterable): Tp.Tuple<[Iterable, Iterable]> => A.separate(Array.from(map_(as, f))) } /** * Infinite sequence produced by repeated application of f to a */ export function unfold(a: A, f: (a: A) => A): Iterable { return { *[Symbol.iterator]() { yield a let current = a while (true) { current = f(a) yield current } } } } export function corresponds( left: Iterable, right: Iterable, f: (a: A, b: B) => boolean ) { const leftIt = left[Symbol.iterator]() const rightIt = right[Symbol.iterator]() // eslint-disable-next-line no-constant-condition while (1) { const lnext = leftIt.next() const rnext = rightIt.next() if (lnext.done !== rnext.done) { return false } if (lnext.done) { return true } if (!f(lnext.value, rnext.value)) { return false } } throw new Error("Bug") }