import * as _Alt from 'fp-ts/Alt' import * as Alte from 'fp-ts/Alternative' import * as Appli from 'fp-ts/Applicative' import * as _Apply from 'fp-ts/Apply' import * as Ch from 'fp-ts/Chain' import * as Co from 'fp-ts/Compactable' import * as Ei from 'fp-ts/Either' import * as Eq from 'fp-ts/Eq' import * as Ex from 'fp-ts/Extend' import * as Fi from 'fp-ts/Filterable' import * as FiWI from 'fp-ts/FilterableWithIndex' import * as Fo from 'fp-ts/Foldable' import * as FoWI from 'fp-ts/FoldableWithIndex' import * as FIO from 'fp-ts/FromIO' import { constFalse, constTrue, flip, flow, identity, Lazy, pipe, } from 'fp-ts/function' import * as Fu from 'fp-ts/Functor' import * as FuWI from 'fp-ts/FunctorWithIndex' import { HKT, Kind, Kind2, Kind3, Kind4, URIS, URIS2, URIS3, URIS4, } from 'fp-ts/HKT' import * as IO from 'fp-ts/IO' import * as Mona from 'fp-ts/Monad' import * as MIO from 'fp-ts/MonadIO' import * as Mono from 'fp-ts/Monoid' import * as Op from 'fp-ts/Option' import * as Or from 'fp-ts/Ord' import * as P from 'fp-ts/Pointed' import * as Pr from 'fp-ts/Predicate' import * as R from 'fp-ts/Random' import * as RA from 'fp-ts/ReadonlyArray' import * as RNEA from 'fp-ts/ReadonlyNonEmptyArray' import * as RR from 'fp-ts/ReadonlyRecord' import * as Re from 'fp-ts/Refinement' import * as S from 'fp-ts/Separated' import * as T from 'fp-ts/Traversable' import * as TWI from 'fp-ts/TraversableWithIndex' import * as U from 'fp-ts/Unfoldable' import * as W from 'fp-ts/Witherable' import { Int } from 'io-ts' import { curry } from './function' export const URI = 'Yield' export type URI = typeof URI declare module 'fp-ts/HKT' { interface URItoKind { [URI]: Yield } } export type Yield = Lazy> export const getMonoid = (): Mono.Monoid> => ({ empty: fromReadonlyArray([]), concat: (x, y) => function* () { yield* x() yield* y() }, }) export const getEq = (E: Eq.Eq): Eq.Eq> => Eq.fromEquals((x, y) => { const bs = y() for (const a of x()) { const b = bs.next() if (b.done || !E.equals(a, b.value)) { return false } } return Boolean(bs.next().done) }) export const getOrd = (O: Or.Ord): Or.Ord> => Or.fromCompare((first, second) => { const bs = second() for (const a of first()) { const b = bs.next() if (b.done) { return 1 } const ordering = O.compare(a, b.value) if (0 !== ordering) { return ordering } } return !bs.next().done ? -1 : 0 }) export const Functor: Fu.Functor1 = { URI, map: (fa, f) => function* () { for (const a of fa()) { yield f(a) } }, } export const map = curry(flip(Functor.map)) export const flap = Fu.flap(Functor) export const bindTo = Fu.bindTo(Functor) export const Pointed: P.Pointed1 = { URI, of: (a) => function* () { yield a }, } export const of = Pointed.of export const Do = Pointed.of({}) export const FunctorWithIndex: FuWI.FunctorWithIndex1 = { ...Functor, mapWithIndex: (fa, f) => Functor.map(pipe(fa, zip(range(0))), ([a, i]) => f(i, a)), } export const mapWithIndex = curry(flip(FunctorWithIndex.mapWithIndex)) export const Apply: _Apply.Apply1 = { ...Functor, ap: (fab, fa) => Chain.chain(fab, curry(Functor.map)(fa)), } export const ap = curry(flip(Apply.ap)) export const apFirst = _Apply.apFirst(Apply) export const apSecond = _Apply.apSecond(Apply) export const apS = _Apply.apS(Apply) export const Applicative: Appli.Applicative1 = { ...Pointed, ...Apply } export const Chain: Ch.Chain1 = { ...Apply, chain: (fa, f) => flatten(Functor.map(fa, f)), } export const chain = curry(flip(Chain.chain)) export const chainFirst = Ch.chainFirst(Chain) export const bind = Ch.bind(Chain) export const chainWithIndex = (f: (i: number, a: A) => Yield) => (fa: Yield): Yield => pipe( fa, zip(range(0)), chain(([a, i]) => f(i, a)), ) export const Monad: Mona.Monad1 = { ...Applicative, ...Chain } export const FromIO: FIO.FromIO1 = { URI, fromIO: (fa) => pipe(range(0), map(fa)), } export const fromIO = FromIO.fromIO export const fromIOK = FIO.fromIOK(FromIO) export const chainIOK = FIO.chainIOK(FromIO, Chain) export const chainFirstIOK = FIO.chainFirstIOK(FromIO, Chain) export const MonadIO: MIO.MonadIO1 = { ...Monad, ...FromIO } export const Unfoldable: U.Unfoldable1 = { URI, unfold: (b, f) => function* () { for ( let _b = b, ab = f(_b); Op.isSome(ab); _b = ab.value[1], ab = f(_b) ) { yield ab.value[0] } }, } export const unfold = curry(flip(Unfoldable.unfold)) export const Alt: _Alt.Alt1 = { ...Functor, alt: (fa: Yield, that: Lazy>) => getMonoid().concat(fa, that()), } export const alt = curry(flip(Alt.alt)) export const Alternative: Alte.Alternative1 = { ...Applicative, ...Alt, zero: () => getMonoid().empty, } export const zero = Alternative.zero export const Extend: Ex.Extend1 = { ...Functor, extend: (wa, f) => pipe( wa, mapWithIndex((i) => pipe(wa, dropLeft(i), f)), ), } export const extend = curry(flip(Extend.extend)) export const duplicate = (fa: Yield) => pipe(fa, extend(identity)) export const Compactable: Co.Compactable1 = { URI, compact: (fa) => Functor.map(Filterable.filter(fa, Op.isSome), (a) => a.value), separate: (fa) => S.separated( Functor.map(Filterable.filter(fa, Ei.isLeft), (a) => a.left), Functor.map(Filterable.filter(fa, Ei.isRight), (a) => a.right), ), } export const compact = Compactable.compact export const separate = Compactable.separate function _filter( fa: Yield, refinement: Re.Refinement, ): Yield function _filter(fa: Yield, predicate: Pr.Predicate): Yield function _filter(fa: Yield, predicate: Pr.Predicate) { return function* () { for (const a of fa()) { if (!predicate(a)) { continue } yield a } } } function _partition( fa: Yield, refinement: Re.Refinement, ): S.Separated, Yield> function _partition( fa: Yield, predicate: Pr.Predicate, ): S.Separated, Yield> function _partition(fa: Yield, predicate: Pr.Predicate) { return S.separated( Filterable.filter(fa, Pr.not(predicate)), Filterable.filter(fa, predicate), ) } export const Filterable: Fi.Filterable1 = { ...Functor, ...Compactable, filter: _filter, filterMap: (fa, f) => Compactable.compact(Functor.map(fa, f)), partition: _partition, partitionMap: (fa, f) => Compactable.separate(Functor.map(fa, f)), } export function filter( refinement: Re.Refinement, ): (fa: Yield) => Yield export function filter( predicate: Pr.Predicate, ): (fa: Yield) => Yield export function filter(predicate: Pr.Predicate) { return (fa: Yield) => Filterable.filter(fa, predicate) } export const filterMap = curry(flip(Filterable.filterMap)) export function partition( refinement: Re.Refinement, ): (fa: Yield) => S.Separated, Yield> export function partition( predicate: Pr.Predicate, ): (fa: Yield) => S.Separated, Yield> export function partition(predicate: Pr.Predicate) { return (fa: Yield) => Filterable.partition(fa, predicate) } export const partitionMap = curry(flip(Filterable.partitionMap)) function _filterWithIndex( fa: Yield, refinementWithIndex: (i: number, a: A) => a is B, ): Yield function _filterWithIndex( fa: Yield, predicateWithIndex: (i: number, a: A) => boolean, ): Yield function _filterWithIndex( fa: Yield, predicateWithIndex: (i: number, a: A) => boolean, ) { return Compactable.compact( FunctorWithIndex.mapWithIndex(fa, (i, a) => predicateWithIndex(i, a) ? Op.some(a) : Op.none, ), ) } function _partitionWithIndex( fa: Yield, refinementWithIndex: (i: number, a: A) => a is B, ): S.Separated, Yield> function _partitionWithIndex( fa: Yield, predicateWithIndex: (i: number, a: A) => boolean, ): S.Separated, Yield> function _partitionWithIndex( fa: Yield, predicateWithIndex: (i: number, a: A) => boolean, ) { return Compactable.separate( FunctorWithIndex.mapWithIndex(fa, (i, a) => predicateWithIndex(i, a) ? Ei.right(a) : Ei.left(a), ), ) } export const FilterableWithIndex: FiWI.FilterableWithIndex1 = { ...FunctorWithIndex, ...Filterable, filterWithIndex: _filterWithIndex, filterMapWithIndex: (fa, f) => Compactable.compact(FunctorWithIndex.mapWithIndex(fa, f)), partitionWithIndex: _partitionWithIndex, partitionMapWithIndex: (fa, f) => Compactable.separate(FunctorWithIndex.mapWithIndex(fa, f)), } export function filterWithIndex( refinementWithIndex: (i: number, a: A) => a is B, ): (fa: Yield) => Yield export function filterWithIndex( predicateWithIndex: (i: number, a: A) => boolean, ): (fa: Yield) => Yield export function filterWithIndex( predicateWithIndex: (i: number, a: A) => boolean, ) { return (fa: Yield) => FilterableWithIndex.filterWithIndex(fa, predicateWithIndex) } export const filterMapWithIndex = curry( flip(FilterableWithIndex.filterMapWithIndex), ) export function partitionWithIndex( refinementWithIndex: (i: number, a: A) => a is B, ): (fa: Yield) => S.Separated, Yield> export function partitionWithIndex( predicateWithIndex: (i: number, a: A) => boolean, ): (fa: Yield) => S.Separated, Yield> export function partitionWithIndex( predicateWithIndex: (i: number, a: A) => boolean, ) { return (fa: Yield) => FilterableWithIndex.partitionWithIndex(fa, predicateWithIndex) } export const partitionMapWithIndex = curry( flip(FilterableWithIndex.partitionMapWithIndex), ) export const Foldable: Fo.Foldable1 = { URI, reduce: (fa, b, f) => FoldableWithIndex.reduceWithIndex(fa, b, (_, _b, a) => f(_b, a)), foldMap: (M) => (fa, f) => FoldableWithIndex.foldMapWithIndex(M)(fa, (_, a) => f(a)), reduceRight: (fa, b, f) => FoldableWithIndex.reduceRightWithIndex(fa, b, (_, a, _b) => f(a, _b)), } export const reduce = (b: B, f: (b: B, a: A) => B) => (fa: Yield) => Foldable.reduce(fa, b, f) export const foldMap = (M: Mono.Monoid) => (f: (a: A) => M) => (fa: Yield) => Foldable.foldMap(M)(fa, f) export const reduceRight = (b: B, f: (a: A, b: B) => B) => (fa: Yield) => Foldable.reduceRight(fa, b, f) export const FoldableWithIndex: FoWI.FoldableWithIndex1 = { ...Foldable, reduceWithIndex: (fa, b, f) => pipe(fa, zip(range(0)), (_fa) => { let _b = b for (const [a, i] of _fa()) { _b = f(i, _b, a) } return _b }), foldMapWithIndex: (M) => (fa, f) => FoldableWithIndex.reduceWithIndex(fa, M.empty, (i, b, a) => M.concat(b, f(i, a)), ), reduceRightWithIndex: (fa, b, f) => pipe(fa, toReadonlyArray, RA.reduceRightWithIndex(b, f)), } export const reduceWithIndex = (b: B, f: (i: number, b: B, a: A) => B) => (fa: Yield) => FoldableWithIndex.reduceWithIndex(fa, b, f) export const foldMapWithIndex = (M: Mono.Monoid) => (f: (i: number, a: A) => M) => (fa: Yield) => FoldableWithIndex.foldMapWithIndex(M)(fa, f) export const reduceRightWithIndex = (b: B, f: (i: number, a: A, b: B) => B) => (fa: Yield) => FoldableWithIndex.reduceRightWithIndex(fa, b, f) function _traverse( F: Appli.Applicative4, ): ( ta: Yield, f: (a: A) => Kind4, ) => Kind4> function _traverse( F: Appli.Applicative3, ): ( ta: Yield, f: (a: A) => Kind3, ) => Kind3> function _traverse( F: Appli.Applicative3C, ): ( ta: Yield, f: (a: A) => Kind3, ) => Kind3> function _traverse( F: Appli.Applicative2, ): (ta: Yield, f: (a: A) => Kind2) => Kind2> function _traverse( F: Appli.Applicative2C, ): (ta: Yield, f: (a: A) => Kind2) => Kind2> function _traverse( F: Appli.Applicative1, ): (ta: Yield, f: (a: A) => Kind) => Kind> function _traverse(F: Appli.Applicative) { return (ta: Yield, f: (a: A) => HKT) => TraversableWithIndex.traverseWithIndex(F)(ta, (_: number, a: A) => f(a)) } export function sequence( F: Appli.Applicative4, ): (ta: Yield>) => Kind4> export function sequence( F: Appli.Applicative3, ): (ta: Yield>) => Kind3> export function sequence( F: Appli.Applicative3C, ): (ta: Yield>) => Kind3> export function sequence( F: Appli.Applicative2, ): (ta: Yield>) => Kind2> export function sequence( F: Appli.Applicative2C, ): (ta: Yield>) => Kind2> export function sequence( F: Appli.Applicative1, ): (ta: Yield>) => Kind> export function sequence( F: Appli.Applicative, ): (ta: Yield>) => HKT> { return (ta: Yield>) => Traversable.traverse(F)(ta, identity) } export const Traversable: T.Traversable1 = { ...Functor, ...Foldable, traverse: _traverse, sequence, } export function traverse( F: Appli.Applicative4, ): ( f: (a: A) => Kind4, ) => (ta: Yield) => Kind4> export function traverse( F: Appli.Applicative3, ): ( f: (a: A) => Kind3, ) => (ta: Yield) => Kind3> export function traverse( F: Appli.Applicative3C, ): ( f: (a: A) => Kind3, ) => (ta: Yield) => Kind3> export function traverse( F: Appli.Applicative2, ): ( f: (a: A) => Kind2, ) => (ta: Yield) => Kind2> export function traverse( F: Appli.Applicative2C, ): ( f: (a: A) => Kind2, ) => (ta: Yield) => Kind2> export function traverse( F: Appli.Applicative1, ): (f: (a: A) => Kind) => (ta: Yield) => Kind> export function traverse(F: Appli.Applicative) { return (f: (a: A) => HKT) => (ta: Yield) => Traversable.traverse(F)(ta, f) } function _traverseWithIndex( F: Appli.Applicative4, ): ( ta: Yield, f: (i: number, a: A) => Kind4, ) => Kind4> function _traverseWithIndex( F: Appli.Applicative3, ): ( ta: Yield, f: (i: number, a: A) => Kind3, ) => Kind3> function _traverseWithIndex( F: Appli.Applicative3C, ): ( ta: Yield, f: (i: number, a: A) => Kind3, ) => Kind3> function _traverseWithIndex( F: Appli.Applicative2, ): ( ta: Yield, f: (i: number, a: A) => Kind2, ) => Kind2> function _traverseWithIndex( F: Appli.Applicative2C, ): ( ta: Yield, f: (i: number, a: A) => Kind2, ) => Kind2> function _traverseWithIndex( F: Appli.Applicative1, ): (ta: Yield, f: (i: number, a: A) => Kind) => Kind> function _traverseWithIndex(F: Appli.Applicative) { return (ta: Yield, f: (i: number, a: A) => HKT) => FoldableWithIndex.reduceWithIndex(ta, F.of(zero()), (i, fbs, a) => F.ap( F.map(fbs, (bs) => (b: B) => pipe(bs, append(b))), f(i, a), ), ) } export const TraversableWithIndex: TWI.TraversableWithIndex1 = { ...FunctorWithIndex, ...FoldableWithIndex, ...Traversable, traverseWithIndex: _traverseWithIndex, } export function traverseWithIndex( F: Appli.Applicative4, ): ( f: (i: number, a: A) => Kind4, ) => (ta: Yield) => Kind4> export function traverseWithIndex( F: Appli.Applicative3, ): ( f: (i: number, a: A) => Kind3, ) => (ta: Yield) => Kind3> export function traverseWithIndex( F: Appli.Applicative3C, ): ( f: (i: number, a: A) => Kind3, ) => (ta: Yield) => Kind3> export function traverseWithIndex( F: Appli.Applicative2, ): ( f: (i: number, a: A) => Kind2, ) => (ta: Yield) => Kind2> export function traverseWithIndex( F: Appli.Applicative2C, ): ( f: (i: number, a: A) => Kind2, ) => (ta: Yield) => Kind2> export function traverseWithIndex( F: Appli.Applicative1, ): ( f: (i: number, a: A) => Kind, ) => (ta: Yield) => Kind> export function traverseWithIndex(F: Appli.Applicative) { return (f: (i: number, a: A) => HKT) => (ta: Yield) => TraversableWithIndex.traverseWithIndex(F)(ta, f) } function _wilt( F: Appli.Applicative3, ): ( wa: Yield, f: (a: A) => Kind3>, ) => Kind3, Yield>> function _wilt( F: Appli.Applicative3C, ): ( wa: Yield, f: (a: A) => Kind3>, ) => Kind3, Yield>> function _wilt( F: Appli.Applicative2, ): ( wa: Yield, f: (a: A) => Kind2>, ) => Kind2, Yield>> function _wilt( F: Appli.Applicative2C, ): ( wa: Yield, f: (a: A) => Kind2>, ) => Kind2, Yield>> function _wilt( F: Appli.Applicative1, ): ( wa: Yield, f: (a: A) => Kind>, ) => Kind, Yield>> function _wilt(F: Appli.Applicative) { return (wa: Yield, f: (a: A) => HKT>) => F.map(Traversable.traverse(F)(wa, f), separate) } function _wither( F: Appli.Applicative3, ): ( ta: Yield, f: (a: A) => Kind3>, ) => Kind3> function _wither( F: Appli.Applicative3C, ): ( ta: Yield, f: (a: A) => Kind3>, ) => Kind3> function _wither( F: Appli.Applicative2, ): ( ta: Yield, f: (a: A) => Kind2>, ) => Kind2> function _wither( F: Appli.Applicative2C, ): ( ta: Yield, f: (a: A) => Kind2>, ) => Kind2> function _wither( F: Appli.Applicative1, ): (ta: Yield, f: (a: A) => Kind>) => Kind> function _wither(F: Appli.Applicative) { return (ta: Yield, f: (a: A) => HKT>) => F.map(Traversable.traverse(F)(ta, f), compact) } export const Witherable: W.Witherable1 = { ...Traversable, ...Filterable, wilt: _wilt, wither: _wither, } export function wilt( F: Appli.Applicative3, ): ( f: (a: A) => Kind3>, ) => (wa: Yield) => Kind3, Yield>> export function wilt( F: Appli.Applicative3C, ): ( f: (a: A) => Kind3>, ) => (wa: Yield) => Kind3, Yield>> export function wilt( F: Appli.Applicative2, ): ( f: (a: A) => Kind2>, ) => (wa: Yield) => Kind2, Yield>> export function wilt( F: Appli.Applicative2C, ): ( f: (a: A) => Kind2>, ) => (wa: Yield) => Kind2, Yield>> export function wilt( F: Appli.Applicative1, ): ( f: (a: A) => Kind>, ) => (wa: Yield) => Kind, Yield>> export function wilt(F: Appli.Applicative) { return (f: (a: A) => HKT>) => (wa: Yield) => Witherable.wilt(F)(wa, f) } export function wither( F: Appli.Applicative3, ): ( f: (a: A) => Kind3>, ) => (ta: Yield) => Kind3> export function wither( F: Appli.Applicative3C, ): ( f: (a: A) => Kind3>, ) => (ta: Yield) => Kind3> export function wither( F: Appli.Applicative2, ): ( f: (a: A) => Kind2>, ) => (ta: Yield) => Kind2> export function wither( F: Appli.Applicative2C, ): ( f: (a: A) => Kind2>, ) => (ta: Yield) => Kind2> export function wither( F: Appli.Applicative1, ): ( f: (a: A) => Kind>, ) => (ta: Yield) => Kind> export function wither(F: Appli.Applicative) { return (f: (a: A) => HKT>) => (ta: Yield) => Witherable.wither(F)(ta, f) } export const makeBy = (f: (i: number) => A): Yield => function* () { for (let i = 0; true; i++) { yield f(i) } } export const range = (start: number, end = Infinity): Yield => pipe( makeBy((i) => start + i), takeLeftWhile((n) => n <= Math.max(start, end)), ) export const replicate = (a: A): Yield => makeBy(() => a) export const fromReadonlyArray = (as: ReadonlyArray): Yield => function* () { yield* as } export const fromReadonlyRecord = RR.toUnfoldable(Unfoldable) export const random: Yield = fromIO(R.random) export const randomInt = (low: number, high: number): Yield => fromIO( R.randomInt( Math.floor(low), Math.max(Math.floor(low), Math.floor(high)), ) as IO.IO, ) export const randomRange = (min: number, max: number): Yield => fromIO(R.randomRange(min, Math.max(min, max))) export const randomBool: Yield = fromIO(R.randomBool) export const randomElem = (as: RNEA.ReadonlyNonEmptyArray): Yield => fromIO(R.randomElem(as)) export const prime: Yield = pipe( range(2), sieve((init, n) => pipe( init, RA.every((_n) => 0 !== n % _n), ), ), ) export const exp: Yield = makeBy((n) => Math.exp(n)) export const fibonacci: Yield = function* () { for (let ns = [1, 0] as [number, number]; true; ns = [ns[1], ns[0] + ns[1]]) { yield ns[1] } } export const flatten = (mma: Yield>): Yield => function* () { for (const ma of mma()) { yield* ma() } } export const prepend = (a: A) => (ma: Yield): Yield => function* () { yield a yield* ma() } export const append = (a: A) => (ma: Yield): Yield => function* () { yield* ma() yield a } export const takeLeft = (n: number) => (ma: Yield): Yield => function* () { for (const [a, i] of pipe(ma, zip(range(0)))()) { if (i >= n) { break } yield a } } export const takeRight = (n: number) => (ma: Yield): Yield => pipe(ma, toReadonlyArray, RA.takeRight(n), fromReadonlyArray) export function takeLeftWhile( refinement: Re.Refinement, ): (ma: Yield) => Yield export function takeLeftWhile( predicate: Pr.Predicate, ): (ma: Yield) => Yield export function takeLeftWhile(predicate: Pr.Predicate) { return (ma: Yield) => function* () { for (const a of ma()) { if (!predicate(a)) { break } yield a } } } export const dropLeft = (n: number) => (ma: Yield): Yield => function* () { for (const [a, i] of pipe(ma, zip(range(0)))()) { if (i < n) { continue } yield a } } export const dropRight = (n: number) => (ma: Yield): Yield => pipe(ma, toReadonlyArray, RA.dropRight(n), fromReadonlyArray) export function dropLeftWhile( refinement: Re.Refinement, ): (ma: Yield) => Yield export function dropLeftWhile( predicate: Pr.Predicate, ): (ma: Yield) => Yield export function dropLeftWhile(predicate: Pr.Predicate) { return (ma: Yield) => function* () { const as = ma() let a = as.next() while (!a.done && predicate(a.value)) { a = as.next() } if (!a.done) { yield a.value } yield* as } } export const scanLeft = (b: B, f: (b: B, a: A) => B) => (ma: Yield): Yield => function* () { yield b let _b = b for (const a of ma()) { _b = f(_b, a) yield _b } } export const scanRight = (b: B, f: (a: A, b: B) => B) => (ma: Yield): Yield => pipe( ma, scanLeft(b, (b, a) => f(a, b)), reverse, ) export function sieve(f: (init: ReadonlyArray, a: A) => boolean) { return (ma: Yield): Yield => function* () { const init: Array = [] for (const a of ma()) { if (!f(init, a)) { continue } init.push(a) yield a } } } export const uniq = (E: Eq.Eq) => sieve((init, a) => !pipe(init, RA.elem(E)(a))) export const sort = (O: Or.Ord) => sortBy([O]) export const sortBy = (Os: ReadonlyArray>) => (ma: Yield): Yield => pipe(ma, toReadonlyArray, RA.sortBy(Os), fromReadonlyArray) export const reverse = (ma: Yield): Yield => function* () { const as = toReadonlyArray(ma) for (let i = as.length - 1; i >= 0; i--) { yield as[i] } } export const zipWith = (mb: Yield, f: (a: A, b: B) => C) => (ma: Yield): Yield => function* () { const bs = mb() for (const a of ma()) { const b = bs.next() if (b.done) { break } yield f(a, b.value) } } export const zip = (mb: Yield) => (ma: Yield): Yield> => pipe( ma, zipWith(mb, (a, b) => [a, b] as const), ) export const rights = (ma: Yield>): Yield => pipe(ma, filterMap(Op.fromEither)) export const lefts = (ma: Yield>): Yield => pipe( ma, filter(Ei.isLeft), map((e) => e.left), ) export const prependAll = (middle: A) => (ma: Yield): Yield => pipe( ma, matchLeft( () => ma, () => function* () { for (const a of ma()) { yield middle yield a } }, ), ) export const intersperse = (middle: A) => flow(prependAll(middle), dropLeft(1)) export const rotate = (n: number) => (ma: Yield): Yield => pipe(ma, toReadonlyArray, RA.rotate(n), fromReadonlyArray) export const chop = (f: (ma: Yield) => Readonly<[B, Yield]>) => (ma: Yield): Yield => function* () { for ( let _isNonEmpty = isNonEmpty(ma), [b, _ma] = f(ma); _isNonEmpty; _isNonEmpty = isNonEmpty(_ma), [b, _ma] = f(_ma) ) { yield b } } export const chunksOf = (n: number) => (ma: Yield): Yield> => pipe(ma, chop(splitAt(Math.max(1, n)))) export const matchLeft = (onEmpty: Lazy, onNonEmpty: (head: A, tail: Yield) => B) => (ma: Yield): B => pipe(ma().next(), (a) => a.done ? onEmpty() : onNonEmpty(a.value, pipe(ma, dropLeft(1))), ) export const matchRight = (onEmpty: Lazy, onNonEmpty: (init: Yield, last: A) => B) => (ma: Yield): B => pipe( ma, zip(range(0)), last, Op.match(onEmpty, ([a, i]) => onNonEmpty( pipe( ma, filterWithIndex((_i) => i !== _i), ), a, ), ), ) export const toReadonlyArray = (ma: Yield): ReadonlyArray => [...ma()] export const lookup = (i: number) => (ma: Yield): Op.Option => i < 0 ? Op.none : pipe( ma, dropLeft(i), matchLeft(() => Op.none, Op.some), ) export const head = lookup(0) export const last = (ma: Yield): Op.Option => { let last: Op.Option = Op.none for (const a of ma()) { last = Op.some(a) } return last } export const tail = (ma: Yield): Op.Option> => pipe( ma, matchLeft( () => Op.none, (_, tail) => Op.some(tail), ), ) export const init = (ma: Yield): Op.Option> => pipe( ma, matchRight(() => Op.none, Op.some), ) export interface Spanned { init: Yield rest: Yield } export function spanLeft( refinement: Re.Refinement, ): (ma: Yield) => Spanned export function spanLeft( predicate: Pr.Predicate, ): (ma: Yield) => Spanned export function spanLeft(predicate: Pr.Predicate) { return (ma: Yield) => { const i = pipe( ma, findFirstIndex(Pr.not(predicate)), Op.getOrElse(() => Infinity), ) const [init, rest] = pipe(ma, splitAt(i)) return { init, rest } } } export const splitAt = (n: number) => (ma: Yield): Readonly<[Yield, Yield]> => [pipe(ma, takeLeft(n)), pipe(ma, dropLeft(n))] export const unzip = ( mab: Yield>, ): Readonly<[Yield, Yield]> => [ pipe( mab, map(([a]) => a), ), pipe( mab, map(([_, b]) => b), ), ] export const isEmpty = matchLeft(constTrue, constFalse) export const isNonEmpty = Pr.not(isEmpty) export const size = flow( mapWithIndex(identity), last, Op.match( () => 0, (i) => 1 + i, ), ) export const isOutOfBound = (n: number) => (ma: Yield): boolean => n >= 0 && n < size(ma) export function findFirst( refinement: Re.Refinement, ): (ma: Yield) => Op.Option export function findFirst( predicate: Pr.Predicate, ): (ma: Yield) => Op.Option export function findFirst(predicate: Pr.Predicate) { return (ma: Yield) => { for (const a of ma()) { if (predicate(a)) { return Op.some(a) } } return Op.none } } export const findFirstMap = (f: (a: A) => Op.Option) => (ma: Yield): Op.Option => { for (const a of ma()) { const b = f(a) if (Op.isSome(b)) { return b } } return Op.none } export const findFirstIndex = (predicate: Pr.Predicate) => (ma: Yield): Op.Option => pipe( ma, zip(range(0)), findFirst(([a]) => predicate(a)), Op.map(([_, i]) => i), ) export function findLast( refinement: Re.Refinement, ): (ma: Yield) => Op.Option export function findLast( predicate: Pr.Predicate, ): (ma: Yield) => Op.Option export function findLast(predicate: Pr.Predicate) { return (ma: Yield) => pipe(ma, reverse, findFirst(predicate)) } export const findLastMap = (f: (a: A) => Op.Option) => (ma: Yield): Op.Option => pipe(ma, reverse, findFirstMap(f)) export const findLastIndex = (predicate: Pr.Predicate) => (ma: Yield): Op.Option => pipe( ma, zip(range(0)), findLastMap(([a, i]) => (predicate(a) ? Op.some(i) : Op.none)), ) export const elem = (Eq: Eq.Eq) => (a: A) => findFirst((_a: A) => Eq.equals(a, _a)) export const insertAt = (i: number, a: A) => (ma: Yield): Op.Option> => pipe( ma, lookup(i), Op.map( () => function* () { for (const [_a, _i] of pipe(ma, zip(range(0)))()) { if (i === _i) { yield a } yield _a } }, ), ) export const modifyAt = (i: number, f: (a: A) => A) => (ma: Yield): Op.Option> => pipe( ma, lookup(i), Op.map(() => pipe( ma, mapWithIndex((_i, a) => (i === _i ? f(a) : a)), ), ), ) export const updateAt = (i: number, a: A) => modifyAt(i, () => a) export const deleteAt = (i: number) => (ma: Yield): Op.Option> => pipe( ma, lookup(i), Op.map(() => pipe( ma, filterWithIndex((_i) => i !== _i), ), ), )