├── src ├── index.ts ├── AsyncStream │ ├── utils │ │ ├── maybe-async.ts │ │ ├── update-at.ts │ │ ├── size.ts │ │ ├── flatten.ts │ │ ├── replicate.ts │ │ ├── async-predicate.ts │ │ ├── reverse.ts │ │ ├── range.ts │ │ ├── get-race-monoid.ts │ │ ├── lefts.ts │ │ ├── make-by.ts │ │ ├── rights.ts │ │ ├── duplicate.ts │ │ ├── head.ts │ │ ├── last.ts │ │ ├── tail.ts │ │ ├── append-all.ts │ │ ├── prepend-all.ts │ │ ├── delay.ts │ │ ├── is-empty.ts │ │ ├── scan-left.ts │ │ ├── find-last-map.ts │ │ ├── delete-at.ts │ │ ├── find-first-map.ts │ │ ├── init.ts │ │ ├── intersperse.ts │ │ ├── some.ts │ │ ├── uniq.ts │ │ ├── flap.ts │ │ ├── zip-with.ts │ │ ├── modify-at.ts │ │ ├── prepend.ts │ │ ├── append.ts │ │ ├── count-by.ts │ │ ├── take-right.ts │ │ ├── cartesian.ts │ │ ├── scan-right.ts │ │ ├── drop-left.ts │ │ ├── none.ts │ │ ├── lookup.ts │ │ ├── every.ts │ │ ├── drop-right.ts │ │ ├── intercalate.ts │ │ ├── take-left.ts │ │ ├── chop.ts │ │ ├── elem.ts │ │ ├── union.ts │ │ ├── match.ts │ │ ├── comprehension.ts │ │ ├── find-last.ts │ │ ├── difference.ts │ │ ├── min-max.ts │ │ ├── intersection.ts │ │ └── drop-left-while.ts │ ├── monad.ts │ ├── monad-io.ts │ ├── transformations │ │ ├── from-stream.ts │ │ ├── from-option.ts │ │ ├── magma.ts │ │ ├── from-task.ts │ │ ├── from-io.ts │ │ ├── from-readable.ts │ │ ├── from-either.ts │ │ ├── monoid.ts │ │ └── from-async-predicate.ts │ ├── pointed.ts │ ├── monad-task.ts │ ├── uri.ts │ ├── applicative-seq.ts │ ├── apply.ts │ ├── concat.ts │ ├── unfoldable.ts │ ├── extend.ts │ ├── zero.ts │ ├── applicative.ts │ ├── compactable.ts │ └── alternative.ts └── Stream │ ├── utils │ ├── size.ts │ ├── reverse.ts │ ├── update-at.ts │ ├── replicate.ts │ ├── range.ts │ ├── flatten.ts │ ├── head.ts │ ├── last.ts │ ├── tail.ts │ ├── lefts.ts │ ├── make-by.ts │ ├── rights.ts │ ├── duplicate.ts │ ├── is-empty.ts │ ├── append-all.ts │ ├── prepend-all.ts │ ├── find-first-map.ts │ ├── find-last-map.ts │ ├── init.ts │ ├── scan-left.ts │ ├── flap.ts │ ├── delete-at.ts │ ├── intersperse.ts │ ├── some.ts │ ├── uniq.ts │ ├── zip-with.ts │ ├── modify-at.ts │ ├── prepend.ts │ ├── split-at.ts │ ├── append.ts │ ├── count-by.ts │ ├── chunks-of.ts │ ├── none.ts │ ├── take-right.ts │ ├── scan-right.ts │ ├── cartesian.ts │ ├── drop-left.ts │ ├── every.ts │ ├── drop-right.ts │ ├── intercalate.ts │ ├── lookup.ts │ ├── rotate.ts │ ├── take-left.ts │ ├── chop.ts │ ├── transpose.ts │ ├── match.ts │ ├── elem.ts │ ├── union.ts │ ├── find-last.ts │ ├── comprehension.ts │ ├── find-first.ts │ ├── min-max.ts │ ├── difference.ts │ ├── match-left.ts │ ├── intersection.ts │ ├── drop-left-while.ts │ ├── zip.ts │ └── zip-array.ts │ ├── monad.ts │ ├── transformations │ ├── from-option.ts │ ├── magma.ts │ ├── from-io.ts │ ├── show.ts │ ├── from-either.ts │ ├── eq.ts │ ├── monoid.ts │ └── from-predicate.ts │ ├── uri.ts │ ├── pointed.ts │ ├── apply.ts │ ├── zero.ts │ ├── conversions.ts │ ├── unfoldable.ts │ ├── concat.ts │ ├── applicative.ts │ ├── extend.ts │ ├── traversable-array.ts │ ├── compactable.ts │ └── alternative.ts ├── .gitignore ├── .npmignore ├── tsconfig.json ├── tsconfig-cjs.json ├── fixup.sh ├── tsconfig-base.json ├── README.md └── CHANGELOG.md /src/index.ts: -------------------------------------------------------------------------------- 1 | export * as stream from './Stream' 2 | export * as asyncStream from './AsyncStream' 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *lock.y[a]ml 3 | .DS_Store 4 | dist 5 | release.sh 6 | launch.json 7 | .release-it.json 8 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/maybe-async.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Describes that the type passed might be sync or async. 3 | */ 4 | export type MaybeAsync = T | Promise 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *lock.y[a]ml 2 | release.sh 3 | .release-it.json 4 | *.sh 5 | .git 6 | .gitignore 7 | .vscode 8 | src 9 | *.js.map 10 | *.tar.gz 11 | *.zip 12 | tsconfig*.json 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig-base.json", 3 | "compilerOptions": { 4 | "module": "NodeNext", 5 | "outDir": "dist/esm", 6 | "target": "esnext" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig-cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig-base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "outDir": "dist/cjs", 6 | "target": "es2015" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Stream/utils/size.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from '../uri' 2 | 3 | /** 4 | * Calculates the number of elements in a {@link Stream}. 5 | * 6 | * @export 7 | * @template A The value type. 8 | * @param {Stream} fa The input stream. 9 | * @return {number} The number of elements. 10 | * 11 | * @__PURE__ 12 | */ 13 | export function size(fa: Stream): number { 14 | let i = 0 15 | for (const _ of fa()) i++ 16 | return i 17 | } 18 | -------------------------------------------------------------------------------- /src/Stream/monad.ts: -------------------------------------------------------------------------------- 1 | import { Monad1 } from 'fp-ts/lib/Monad' 2 | 3 | import { Applicative } from './applicative' 4 | import { Chain } from './chain' 5 | import { Functor } from './functor' 6 | import { Pointed } from './pointed' 7 | import { URI } from './uri' 8 | 9 | /** 10 | * The `Monad` category instance for {@link Stream}. 11 | */ 12 | export const Monad: Monad1 = { 13 | URI, 14 | ap: Applicative.ap, 15 | chain: Chain.chain, 16 | map: Functor.map, 17 | of: Pointed.of 18 | } 19 | -------------------------------------------------------------------------------- /src/AsyncStream/monad.ts: -------------------------------------------------------------------------------- 1 | import { Monad1 } from 'fp-ts/lib/Monad' 2 | 3 | import { Applicative } from './applicative' 4 | import { Chain } from './chain' 5 | import { Functor } from './functor' 6 | import { Pointed } from './pointed' 7 | import { URI } from './uri' 8 | 9 | /** 10 | * The `Monad` category instance for {@link AsyncStream}. 11 | */ 12 | export const Monad: Monad1 = { 13 | URI, 14 | ap: Applicative.ap, 15 | chain: Chain.chain, 16 | map: Functor.map, 17 | of: Pointed.of 18 | } 19 | -------------------------------------------------------------------------------- /src/Stream/utils/reverse.ts: -------------------------------------------------------------------------------- 1 | import { fromIterable, toArray } from '../conversions' 2 | import { Stream } from '../uri' 3 | 4 | /** 5 | * Reverses a {@link Stream} and returns another one. 6 | * 7 | * **Warning: This function consumes the stream.** 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @param {Stream} fa The input stream. 12 | * @return {Stream} The output stream. 13 | */ 14 | export function reverse(fa: Stream): Stream { 15 | return fromIterable(toArray(fa).reverse()) 16 | } 17 | -------------------------------------------------------------------------------- /src/Stream/utils/update-at.ts: -------------------------------------------------------------------------------- 1 | import { modifyAt } from './modify-at' 2 | 3 | /** 4 | * Changes the element at the specified index, creating a new {@link Stream}. 5 | * 6 | * @export 7 | * @template A The value type. 8 | * @param {number} i The index of element to modify. 9 | * @param {A} a The value to set. 10 | * @return {Stream} The stream whose value at given 11 | * index is modified. 12 | * 13 | * @__PURE__ 14 | */ 15 | export function updateAt(i: number, a: A) { 16 | return modifyAt(i, (_) => a) 17 | } 18 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/update-at.ts: -------------------------------------------------------------------------------- 1 | import { modifyAt } from './modify-at' 2 | 3 | /** 4 | * Changes the element at the specified index, creating a new 5 | * {@link AsyncStream}. 6 | * 7 | * @export 8 | * @template A The value type. 9 | * @param {number} i The index of element to modify. 10 | * @param {A} a The value to set. 11 | * @return {AsyncStream} The stream whose value at given 12 | * index is modified. 13 | * 14 | * @__PURE__ 15 | */ 16 | export function updateAt(i: number, a: A) { 17 | return modifyAt(i, (_) => a) 18 | } 19 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/size.ts: -------------------------------------------------------------------------------- 1 | import { Task } from 'fp-ts/lib/Task' 2 | 3 | import { AsyncStream } from '../uri' 4 | 5 | /** 6 | * Calculates the number of elements in an {@link AsyncStream}. 7 | * 8 | * @export 9 | * @template A The value type. 10 | * @param {AsyncStream} fa The input async stream. 11 | * @return {number} The number of elements. 12 | * 13 | * @__PURE__ 14 | */ 15 | export function size(fa: AsyncStream): Task { 16 | return async function _size() { 17 | let i = 0 18 | for await (const _ of fa()) i++ 19 | return i 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/AsyncStream/monad-io.ts: -------------------------------------------------------------------------------- 1 | import { MonadIO1 } from 'fp-ts/lib/MonadIO' 2 | 3 | import { Applicative } from './applicative' 4 | import { Chain } from './chain' 5 | import { Functor } from './functor' 6 | import { Pointed } from './pointed' 7 | import { FromIO } from './transformations/from-io' 8 | import { URI } from './uri' 9 | 10 | /** 11 | * The `MonadIO` category instance for {@link AsyncStream}. 12 | */ 13 | export const MonadIO: MonadIO1 = { 14 | URI, 15 | ap: Applicative.ap, 16 | chain: Chain.chain, 17 | fromIO: FromIO.fromIO, 18 | map: Functor.map, 19 | of: Pointed.of, 20 | } 21 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/flatten.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from '../uri' 2 | 3 | /** 4 | * Flattens an {@link AsyncStream} of {@link AsyncStream}s into 5 | * one. 6 | * 7 | * @export 8 | * @template A The value type. 9 | * @param {AsyncStream>} mma The input async streams. 10 | * @return {AsyncStream} The output async stream. 11 | * 12 | * @category sequencing 13 | * @__PURE__ 14 | */ 15 | export function flatten(mma: AsyncStream>): AsyncStream { 16 | return async function* _flatten() { 17 | for await (const ma of mma()) { 18 | yield* ma() 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Stream/utils/replicate.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from '../uri' 2 | 3 | /** 4 | * Creates a {@link Stream} containing a value repeated the specified number 5 | * of times. 6 | * 7 | * *Note:* `n` is normalized to a non negative integer. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @param {number} n The count of the repetition. 12 | * @param {A} a The value to replicate. 13 | * @return {Stream} The output stream. 14 | * 15 | * @__PURE__ 16 | */ 17 | export function replicate(n: number, a: A): Stream { 18 | return function* _replicate() { 19 | while (n-- > 0) yield a 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/AsyncStream/transformations/from-stream.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from '../../Stream/uri' 2 | import { AsyncStream } from '../uri' 3 | 4 | /** 5 | * Creates an {@link AsyncStream} from a {@link Stream} instance. 6 | * 7 | * @export 8 | * @template A The value type. 9 | * @param {Stream} fa The stream instance. 10 | * @return {AsyncStream} The async stream output. 11 | * 12 | * @category conversion 13 | * @__PURE__ 14 | */ 15 | export function fromStream(fa: Stream): AsyncStream { 16 | return async function* _fromStream() { 17 | for (const a of fa()) { 18 | yield a 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Stream/utils/range.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from '../uri' 2 | 3 | /** 4 | * Creates a range between an interval. 5 | * 6 | * *Note:* If `end` is not given, the range will be streaming the numbers 7 | * infinitely. 8 | * 9 | * @export 10 | * @param {number} start The start of the range. 11 | * @param {number} [end] The end of the range. 12 | * @return {Stream} A {@link Stream} of numbers in the interval. 13 | * 14 | * @__PURE__ 15 | */ 16 | export function range(start: number, end: number = Infinity): Stream { 17 | return function* __range() { 18 | while (start < end) yield start++ 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Stream/utils/flatten.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from '../uri' 2 | 3 | /** 4 | * Takes a {@link Stream} of {@link Stream}s of `A` and flattens them into 5 | * a {@link Stream} of `A` by concatenating the elements of each {@link Stream} 6 | * in order. 7 | * 8 | * @export 9 | * @template A The value type. 10 | * @param {Stream>} mma The input streams. 11 | * @return {Stream} The output stream. 12 | * 13 | * @category sequencing 14 | * @__PURE__ 15 | */ 16 | export function flatten(mma: Stream>): Stream { 17 | return function* _flatten() { 18 | for (const ma of mma()) yield* ma() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Stream/utils/head.ts: -------------------------------------------------------------------------------- 1 | import { none, Option, some } from 'fp-ts/lib/Option' 2 | 3 | import { Stream } from '../uri' 4 | 5 | /** 6 | * Gets the first element in a {@link Stream}, or `None` if the {@link Stream} 7 | * is empty. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @param {Stream} ma The input stream. 12 | * @return {Option} An option of the first value in the stream. 13 | * 14 | * @__PURE__ 15 | */ 16 | export function head(ma: Stream): Option { 17 | const gen = ma() 18 | const { value, done } = gen.next() 19 | 20 | if (done) return none 21 | else { 22 | return some(value) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/replicate.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from '../uri' 2 | 3 | /** 4 | * Creates an {@link AsyncStream} containing a value repeated the specified 5 | * number of times. 6 | * 7 | * *Note:* `n` is normalized to a non negative integer. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @param {number} n The count of the repetition. 12 | * @param {A} a The value to replicate. 13 | * @return {AsyncStream} The output stream. 14 | * 15 | * @__PURE__ 16 | */ 17 | export function replicate(n: number, a: A): AsyncStream { 18 | return async function* _replicate() { 19 | while (n-- > 0) yield a 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/async-predicate.ts: -------------------------------------------------------------------------------- 1 | import { MaybeAsync } from './maybe-async' 2 | 3 | /** 4 | * An interface that represents a function that returns a task of a boolean. 5 | * 6 | * @export 7 | * @interface AsyncPredicate 8 | * @template A The value type. 9 | */ 10 | export interface AsyncPredicate { 11 | (a: A): MaybeAsync 12 | } 13 | 14 | /** 15 | * An interface that represents a function that returns a task of a boolean. 16 | * 17 | * @export 18 | * @interface AsyncPredicateWithIndex 19 | * @template A The value type. 20 | */ 21 | export interface AsyncPredicateWithIndex { 22 | (i: I, a: A): MaybeAsync 23 | } 24 | -------------------------------------------------------------------------------- /src/Stream/transformations/from-option.ts: -------------------------------------------------------------------------------- 1 | import { isSome, Option } from 'fp-ts/lib/Option' 2 | 3 | import { of } from '../pointed' 4 | import { Stream } from '../uri' 5 | import { empty } from '../zero' 6 | 7 | /** 8 | * Returns a new {@link Stream} from an {@link Option}. 9 | * 10 | * @export 11 | * @template A The value type. 12 | * @param {Option} fa The option input. 13 | * @return {Stream} The stream output. 14 | * 15 | * @category conversions 16 | * @__PURE__ 17 | */ 18 | export function fromOption(fa: Option): Stream { 19 | if (isSome(fa)) { 20 | return of(fa.value) 21 | } 22 | else { 23 | return empty 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/reverse.ts: -------------------------------------------------------------------------------- 1 | import { toArray } from '../conversions' 2 | import { AsyncStream } from '../uri' 3 | 4 | /** 5 | * Reverses an {@link AsyncStream} and returns another one. 6 | * 7 | * **Warning: This function consumes the stream.** 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @param {AsyncStream} fa The input async stream. 12 | * @return {AsyncStream} The output async stream. 13 | */ 14 | export function reverse(fa: AsyncStream): AsyncStream { 15 | return async function* _reverse() { 16 | const items = await toArray(fa) 17 | for (const item of items.reverse()) { 18 | yield item 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Stream/uri.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Describes a function which returns a {@link Generator} of `A` values. 4 | * 5 | * @export 6 | * @interface Stream 7 | * @template A The value type. 8 | */ 9 | export interface Stream { 10 | (): Generator 11 | } 12 | 13 | /** 14 | * The type URI of the {@link Stream} instances. 15 | * 16 | * @category type lambdas 17 | */ 18 | export const URI = 'fp-ts-stream/Stream' 19 | 20 | /** 21 | * The type URI of the {@link Stream} instances. 22 | * 23 | * @category type lambdas 24 | */ 25 | export type URI = typeof URI 26 | 27 | declare module 'fp-ts/HKT' { 28 | interface URItoKind { 29 | readonly [ URI ]: Stream 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/range.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from '../uri' 2 | 3 | /** 4 | * Creates a range between an interval. 5 | * 6 | * *Note:* If `end` is not given, the range will be streaming the numbers 7 | * infinitely. 8 | * 9 | * @export 10 | * @param {number} start The start of the range. 11 | * @param {number} [end] The end of the range. 12 | * @return {AsyncStream} A {@link AsyncStream} of numbers in the 13 | * interval. 14 | * 15 | * @__PURE__ 16 | */ 17 | export function range(start: number, end: number = Infinity): AsyncStream { 18 | return async function* __range() { 19 | let from = start 20 | while (from < end) yield from++ 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Stream/pointed.ts: -------------------------------------------------------------------------------- 1 | import { Pointed1 } from 'fp-ts/lib/Pointed' 2 | 3 | import { Stream, URI } from './uri' 4 | 5 | /** 6 | * Returns a {@link Stream} of type `A` streaming the given element. 7 | * 8 | * @export 9 | * @template A The value type. 10 | * @param {A} a The value. 11 | * @return {Stream} A {@link Stream} instance of type `A`. 12 | * 13 | * @category model 14 | * @__PURE__ 15 | */ 16 | export function of(a: A): Stream { 17 | return function* _of() { yield a } 18 | } 19 | 20 | /** 21 | * The `Pointed` category instance for {@link Stream}. 22 | * 23 | * @category model 24 | */ 25 | export const Pointed: Pointed1 = { 26 | URI, 27 | of, 28 | } 29 | -------------------------------------------------------------------------------- /src/Stream/utils/last.ts: -------------------------------------------------------------------------------- 1 | import { none, Option, some } from 'fp-ts/lib/Option' 2 | 3 | import { Stream } from '../uri' 4 | 5 | /** 6 | * Gets the last element in a {@link Stream}, or `None` if the {@link Stream} 7 | * is empty. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @param {Stream} fa The input stream. 12 | * @return {Option} An option of the last value. 13 | * 14 | * @__PURE__ 15 | */ 16 | export function last(fa: Stream): Option { 17 | const gen = fa() 18 | const { done, value } = gen.next() 19 | if (done) return none 20 | 21 | let last = value 22 | for (const a of gen) { 23 | last = a 24 | } 25 | 26 | return some(last) 27 | } 28 | -------------------------------------------------------------------------------- /src/AsyncStream/transformations/from-option.ts: -------------------------------------------------------------------------------- 1 | import { isSome, Option } from 'fp-ts/lib/Option' 2 | 3 | import { of } from '../pointed' 4 | import { AsyncStream } from '../uri' 5 | import { empty } from '../zero' 6 | 7 | /** 8 | * Creates an {@link AsyncStream} from an {@link Option} instance. 9 | * 10 | * @export 11 | * @template A The value type. 12 | * @param {Option} fa The option instance. 13 | * @return {AsyncStream} The async stream output. 14 | * 15 | * @category conversions 16 | * @__PURE__ 17 | */ 18 | export function fromOption(fa: Option): AsyncStream { 19 | if (isSome(fa)) { 20 | return of(fa.value) 21 | } 22 | else { 23 | return empty 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/get-race-monoid.ts: -------------------------------------------------------------------------------- 1 | import { Monoid } from 'fp-ts/lib/Monoid' 2 | 3 | import { AsyncStream } from '../uri' 4 | import { never } from '../zero' 5 | 6 | /** 7 | * Monoid returning the first started {@link AsyncStream}. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @return {Monoid>} A monoid instance of async stream. 12 | * 13 | * @category instances 14 | * @__PURE__ 15 | */ 16 | export function getRaceMonoid(): Monoid> { 17 | return { 18 | concat(x, y) { 19 | return async function* _concat() { 20 | yield* await Promise.race([ x(), y() ]) 21 | } 22 | }, 23 | empty: never, 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/AsyncStream/pointed.ts: -------------------------------------------------------------------------------- 1 | import { Pointed1 } from 'fp-ts/lib/Pointed' 2 | 3 | import { AsyncStream, URI } from './uri' 4 | 5 | /** 6 | * Returns a {@link AsyncStream} of type `A` streaming the given element. 7 | * 8 | * @export 9 | * @template A The value type. 10 | * @param {A} a The value. 11 | * @return {AsyncStream} A {@link AsyncStream} instance of type `A`. 12 | * 13 | * @category model 14 | */ 15 | export function of(a: A): AsyncStream { 16 | return async function* _of() { yield a } 17 | } 18 | 19 | /** 20 | * The `Pointed` category instance for {@link AsyncStream}. 21 | * 22 | * @category model 23 | */ 24 | export const Pointed: Pointed1 = { 25 | URI, 26 | of, 27 | } 28 | -------------------------------------------------------------------------------- /src/Stream/transformations/magma.ts: -------------------------------------------------------------------------------- 1 | import { Eq } from 'fp-ts/lib/Eq' 2 | import { Magma } from 'fp-ts/lib/Magma' 3 | 4 | import { Stream } from '../uri' 5 | import { difference } from '../utils/difference' 6 | 7 | /** 8 | * Derives {@link Magma} for {@link Stream}. 9 | * 10 | * @export 11 | * @template A The value type. 12 | * @param {Eq} E The equality instance. 13 | * @return {Magma>} A magma instance of stream of type `A`. 14 | * 15 | * @__PURE__ 16 | */ 17 | export function getDifferenceMagma(E: Eq): Magma> { 18 | const differenceE = difference(E) 19 | return { 20 | concat(first, second) { 21 | return differenceE(second, first) 22 | }, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Stream/utils/tail.ts: -------------------------------------------------------------------------------- 1 | import { none, some } from 'fp-ts/lib/Option' 2 | 3 | import { Stream } from '../uri' 4 | 5 | /** 6 | * Gets all but the first element of a {@link Stream}, creating a new 7 | * {@link Stream}, or `None` if the {@link Stream} is empty. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @param {Stream} fa The input stream. 12 | * @return {Option>} An option of a stream whose first element is 13 | * excluded. 14 | * 15 | * @__PURE__ 16 | */ 17 | export function tail(fa: Stream) { 18 | const gen = fa() 19 | const { done } = gen.next() 20 | 21 | if (done) return none 22 | return some(function* _tail() { 23 | yield* gen 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /src/AsyncStream/monad-task.ts: -------------------------------------------------------------------------------- 1 | import { MonadTask1 } from 'fp-ts/lib/MonadTask' 2 | 3 | import { ApplicativePar } from './applicative' 4 | import { Chain } from './chain' 5 | import { Functor } from './functor' 6 | import { Pointed } from './pointed' 7 | import { FromIO } from './transformations/from-io' 8 | import { FromTask } from './transformations/from-task' 9 | import { URI } from './uri' 10 | 11 | /** 12 | * The `MonadTask` category instance for {@link AsyncStream}. 13 | */ 14 | export const MonadTask: MonadTask1 = { 15 | URI, 16 | ap: ApplicativePar.ap, 17 | map: Functor.map, 18 | chain: Chain.chain, 19 | fromIO: FromIO.fromIO, 20 | fromTask: FromTask.fromTask, 21 | of: Pointed.of, 22 | } 23 | -------------------------------------------------------------------------------- /src/Stream/utils/lefts.ts: -------------------------------------------------------------------------------- 1 | import { Either, isLeft } from 'fp-ts/lib/Either' 2 | 3 | import { Stream } from '../uri' 4 | 5 | /** 6 | * Extracts from a {@link Stream} all the `Left` elements. 7 | * 8 | * All the `Left` elements are extracted in order. 9 | * 10 | * @export 11 | * @template E The error type. 12 | * @template A The value type. 13 | * @param {Stream>} fa The input stream. 14 | * @return {Stream} The output stream. 15 | * 16 | * @__PURE__ 17 | */ 18 | export function lefts(fa: Stream>): Stream { 19 | return function* _lefts() { 20 | for (const e of fa()) { 21 | if (isLeft(e)) { 22 | yield e.left 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Stream/utils/make-by.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from '../uri' 2 | 3 | /** 4 | * Returns a {@link Stream} of length `n` with element `i` initialized with 5 | * `f(i)`. 6 | * 7 | * *Note:* `n` is normalized to a non negative integer. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @param {number} n The number of elements. 12 | * @param {(i: number) => A} f The item making function. 13 | * @return {Stream} The output stream. 14 | * 15 | * @__PURE__ 16 | */ 17 | export function makeBy(n: number, f: (i: number) => A): Stream { 18 | return function* _makeBy() { 19 | if (n < 0) { n = -n } 20 | 21 | for (let i = 0; i < n; ++i) { 22 | yield f(i) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Stream/utils/rights.ts: -------------------------------------------------------------------------------- 1 | import { Either, isRight } from 'fp-ts/lib/Either' 2 | 3 | import { Stream } from '../uri' 4 | 5 | /** 6 | * Extracts from a {@link Stream} all the `Right` elements. 7 | * 8 | * All the `Right` elements are extracted in order. 9 | * 10 | * @export 11 | * @template E The error type. 12 | * @template A The value type. 13 | * @param {Stream>} fa The input stream. 14 | * @return {Stream} The output stream. 15 | * 16 | * @__PURE__ 17 | */ 18 | export function rights(fa: Stream>): Stream { 19 | return function* _rights() { 20 | for (const e of fa()) { 21 | if (isRight(e)) { 22 | yield e.right 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Stream/utils/duplicate.ts: -------------------------------------------------------------------------------- 1 | import { identity } from 'fp-ts/lib/function' 2 | 3 | import { extend } from '../extend' 4 | import { Stream } from '../uri' 5 | 6 | /** 7 | * `duplicate` returns a {@link Stream} containing the whole input 8 | * {@link Stream}, then to the input {@link Stream} dropping the first element, 9 | * then to the input {@link Stream} dropping the first two elements, etc. 10 | * 11 | * @export 12 | * @template A The value type. 13 | * @param {Stream} wa The input stream. 14 | * @return {Stream>} The output stream of streams. 15 | * 16 | * @__PURE__ 17 | */ 18 | export function duplicate(wa: Stream): Stream> { 19 | return extend>(identity)(wa) 20 | } 21 | -------------------------------------------------------------------------------- /src/AsyncStream/uri.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Describes a function which returns a {@link AsyncGenerator} of `A` values. 4 | * 5 | * @export 6 | * @interface AsyncStream 7 | * @template A The value type. 8 | */ 9 | export interface AsyncStream { 10 | (): AsyncGenerator 11 | } 12 | 13 | /** 14 | * The type URI of the {@link AsyncStream} instances. 15 | * 16 | * @category type lambdas 17 | */ 18 | export const URI = 'fp-ts-stream/AsyncStream' 19 | 20 | /** 21 | * The type URI of the {@link AsyncStream} instances. 22 | * 23 | * @category type lambdas 24 | */ 25 | export type URI = typeof URI 26 | 27 | declare module 'fp-ts/HKT' { 28 | interface URItoKind { 29 | readonly [ URI ]: AsyncStream 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/AsyncStream/transformations/magma.ts: -------------------------------------------------------------------------------- 1 | import { Eq } from 'fp-ts/lib/Eq' 2 | import { Magma } from 'fp-ts/lib/Magma' 3 | 4 | import { AsyncStream } from '../uri' 5 | import { difference } from '../utils/difference' 6 | 7 | /** 8 | * Derives {@link Magma} for {@link AsyncStream}. 9 | * 10 | * @export 11 | * @template A The value type. 12 | * @param {Eq} E The equality instance. 13 | * @return {Magma>} A magma instance of async stream of type `A`. 14 | * 15 | * @__PURE__ 16 | */ 17 | export function getDifferenceMagma(E: Eq): Magma> { 18 | const differenceE = difference(E) 19 | return { 20 | concat(first, second) { 21 | return differenceE(second, first) 22 | }, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/lefts.ts: -------------------------------------------------------------------------------- 1 | import { Either, isLeft } from 'fp-ts/lib/Either' 2 | 3 | import { AsyncStream } from '../uri' 4 | 5 | /** 6 | * Extracts from an {@link AsyncStream} all the `Left` elements. 7 | * 8 | * All the `Left` elements are extracted in order. 9 | * 10 | * @export 11 | * @template E The error type. 12 | * @template A The value type. 13 | * @param {AsyncStream>} fa The input async stream. 14 | * @return {AsyncStream} The output async stream. 15 | * 16 | * @__PURE__ 17 | */ 18 | export function lefts(fa: AsyncStream>): AsyncStream { 19 | return async function* _lefts() { 20 | for await (const e of fa()) { 21 | if (isLeft(e)) { 22 | yield e.left 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/make-by.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from '../uri' 2 | 3 | /** 4 | * Returns an {@link AsyncStream} of length `n` with element `i` initialized 5 | * with `f(i)`. 6 | * 7 | * *Note:* `n` is normalized to a non negative integer. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @param {number} n The number of elements. 12 | * @param {(i: number) => A | Promise} f The item making function. 13 | * @return {AsyncStream} The output stream. 14 | * 15 | * @__PURE__ 16 | */ 17 | export function makeBy(n: number, f: (i: number) => A | Promise): AsyncStream { 18 | return async function* _makeBy() { 19 | if (n < 0) { n = -n } 20 | 21 | for (let i = 0; i < n; ++i) { 22 | yield await f(i) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/rights.ts: -------------------------------------------------------------------------------- 1 | import { Either, isRight } from 'fp-ts/lib/Either' 2 | 3 | import { AsyncStream } from '../uri' 4 | 5 | /** 6 | * Extracts from an {@link AsyncStream} all the `Right` elements. 7 | * 8 | * All the `Right` elements are extracted in order. 9 | * 10 | * @export 11 | * @template E The error type. 12 | * @template A The value type. 13 | * @param {AsyncStream>} fa The input async stream. 14 | * @return {AsyncStream} The output async stream. 15 | * 16 | * @__PURE__ 17 | */ 18 | export function rights(fa: AsyncStream>): AsyncStream { 19 | return async function* _rights() { 20 | for await (const e of fa()) { 21 | if (isRight(e)) { 22 | yield e.right 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/AsyncStream/applicative-seq.ts: -------------------------------------------------------------------------------- 1 | import { Applicative1 } from 'fp-ts/lib/Applicative' 2 | import { pipe } from 'fp-ts/lib/function' 3 | 4 | import { chain } from './chain' 5 | import { Functor } from './functor' 6 | import { Pointed } from './pointed' 7 | import { AsyncStream, URI } from './uri' 8 | import { MaybeAsync } from './utils/maybe-async' 9 | 10 | /** 11 | * The `ApplicativeSeq` category instance for {@link AsyncStream}. 12 | */ 13 | export const ApplicativeSeq: Applicative1 = { 14 | URI, 15 | ap: _apSeq, 16 | map: Functor.map, 17 | of: Pointed.of, 18 | } 19 | 20 | function _apSeq(fab: AsyncStream<(a: A) => MaybeAsync>, fa: AsyncStream): AsyncStream { 21 | return pipe( 22 | fab, 23 | chain(f => Functor.map(fa, f as (a: A) => B)) // map also handles promises 24 | ) 25 | } 26 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/duplicate.ts: -------------------------------------------------------------------------------- 1 | import { identity } from 'fp-ts/lib/function' 2 | 3 | import { extend } from '../extend' 4 | import { AsyncStream } from '../uri' 5 | 6 | /** 7 | * `duplicate` returns an {@link AsyncStream} containing the whole input 8 | * {@link AsyncStream}, then to the input {@link AsyncStream} dropping the first 9 | * element, then to the input {@link AsyncStream} dropping the first two 10 | * elements, etc. 11 | * 12 | * @export 13 | * @template A The value type. 14 | * @param {AsyncStream} wa The input async stream. 15 | * @return {AsyncStream>} The output async stream of async 16 | * streams. 17 | * 18 | * @__PURE__ 19 | */ 20 | export function duplicate(wa: AsyncStream): AsyncStream> { 21 | return extend>(identity)(wa) 22 | } 23 | -------------------------------------------------------------------------------- /src/Stream/utils/is-empty.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from '../uri' 2 | 3 | /** 4 | * Tests whether a {@link Stream} is empty. 5 | * 6 | * @export 7 | * @template A The value type. 8 | * @param {Stream} fa The input stream. 9 | * @return {boolean} `true` if the stream was empty, `false` otherwise. 10 | * 11 | * @__PURE__ 12 | */ 13 | export function isEmpty(fa: Stream) { 14 | const gen = fa() 15 | const { done } = gen.next() 16 | return done 17 | } 18 | 19 | /** 20 | * Tests whether a {@link Stream} is not empty. 21 | * 22 | * @export 23 | * @template A The value type. 24 | * @param {Stream} ma The input stream. 25 | * @return {boolean} `true` if the stream was not empty, `false` otherwise. 26 | * 27 | * @__PURE__ 28 | */ 29 | export function isNotEmpty(ma: Stream) { return !isEmpty(ma) } 30 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/head.ts: -------------------------------------------------------------------------------- 1 | import { none, Option, some } from 'fp-ts/lib/Option' 2 | import { Task } from 'fp-ts/lib/Task' 3 | 4 | import { AsyncStream } from '../uri' 5 | 6 | /** 7 | * Gets the first element in an {@link AsyncStream}, or `None` if the 8 | * {@link AsyncStream} is empty. 9 | * 10 | * @export 11 | * @template A The value type. 12 | * @param {AsyncStream} fa The input async stream. 13 | * @return {Task>} A task of an option of the first value in the 14 | * async stream. 15 | * 16 | * @__PURE__ 17 | */ 18 | export function head(fa: AsyncStream): Task> { 19 | return async function _head() { 20 | const gen = fa() 21 | const { value, done } = await gen.next() 22 | 23 | if (done) return none 24 | else { 25 | return some(value) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Stream/transformations/from-io.ts: -------------------------------------------------------------------------------- 1 | import { FromIO1, fromIOK as fromIOFromIOK } from 'fp-ts/lib/FromIO' 2 | import { IO } from 'fp-ts/lib/IO' 3 | 4 | import { Stream, URI } from '../uri' 5 | 6 | /** 7 | * Creates an {@link Stream} from an {@link IO} instance. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @param {IO} fa The io instance. 12 | * @return {Stream} The stream output. 13 | * 14 | * @category conversions 15 | * @__PURE__ 16 | */ 17 | export function fromIO(fa: IO): Stream { 18 | return function* _fromIO() { 19 | yield fa() 20 | } 21 | } 22 | 23 | /** 24 | * The `FromIO` category instance for {@link Stream}. 25 | */ 26 | export const FromIO: FromIO1 = { 27 | URI, 28 | fromIO 29 | } 30 | 31 | /** 32 | * @category lifting 33 | */ 34 | export const fromIOK = fromIOFromIOK(FromIO) 35 | -------------------------------------------------------------------------------- /src/Stream/apply.ts: -------------------------------------------------------------------------------- 1 | import { 2 | apFirst as applyApFirst, 3 | Apply1, 4 | apS as applyApS, 5 | apSecond as applyApSecond, 6 | } from 'fp-ts/lib/Apply' 7 | 8 | import { Applicative } from './applicative' 9 | import { Functor } from './functor' 10 | import { URI } from './uri' 11 | 12 | /** 13 | * The `Apply` category instance for {@link Stream}. 14 | */ 15 | export const Apply: Apply1 = { 16 | URI, 17 | ap: Applicative.ap, 18 | map: Functor.map, 19 | } 20 | 21 | /** 22 | * Combine two effectful actions, keeping only the result of the first. 23 | */ 24 | export const apFirst = applyApFirst(Apply) 25 | 26 | /** 27 | * Combine two effectful actions, keeping only the result of the second. 28 | */ 29 | export const apSecond = applyApSecond(Apply) 30 | 31 | /** 32 | * @category do notation 33 | */ 34 | export const apS = applyApS(Apply) 35 | -------------------------------------------------------------------------------- /src/AsyncStream/transformations/from-task.ts: -------------------------------------------------------------------------------- 1 | import { FromTask1 } from 'fp-ts/lib/FromTask' 2 | import { Task } from 'fp-ts/lib/Task' 3 | 4 | import { AsyncStream, URI } from '../uri' 5 | import { FromIO } from './from-io' 6 | 7 | /** 8 | * Creates an {@link AsyncStream} from a {@link Task} instance. 9 | * 10 | * @export 11 | * @template A The value type. 12 | * @param {Task} fa The task instance. 13 | * @return {AsyncStream} The async stream output. 14 | * 15 | * @category conversions 16 | * @__PURE__ 17 | */ 18 | export function fromTask(fa: Task): AsyncStream { 19 | return async function* _fromTask() { 20 | yield await fa() 21 | } 22 | } 23 | 24 | /** 25 | * The `FromTask` category instance for {@link AsyncStream}. 26 | */ 27 | export const FromTask: FromTask1 = { 28 | URI, 29 | fromIO: FromIO.fromIO, 30 | fromTask, 31 | } 32 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/last.ts: -------------------------------------------------------------------------------- 1 | import { none, Option, some } from 'fp-ts/lib/Option' 2 | import { Task } from 'fp-ts/lib/Task' 3 | 4 | import { AsyncStream } from '../uri' 5 | 6 | /** 7 | * Gets the last element in an {@link AsyncStream}, or `None` if the 8 | * {@link AsyncStream} is empty. 9 | * 10 | * @export 11 | * @template A The value type. 12 | * @param {AsyncStream} fa The input async stream. 13 | * @return {Task>} A task of an option of the last value. 14 | * 15 | * @__PURE__ 16 | */ 17 | export function last(fa: AsyncStream): Task> { 18 | return async function _last() { 19 | const gen = fa() 20 | const { done, value } = await gen.next() 21 | if (done) return none 22 | 23 | let last = value 24 | for await (const a of gen) { 25 | last = a 26 | } 27 | 28 | return some(last) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/tail.ts: -------------------------------------------------------------------------------- 1 | import { none, Option, some } from 'fp-ts/lib/Option' 2 | import { Task } from 'fp-ts/lib/Task' 3 | 4 | import { AsyncStream } from '../uri' 5 | 6 | /** 7 | * Gets all but the first element of an {@link AsyncStream}, creating a new 8 | * {@link AsyncStream}, or `None` if the {@link AsyncStream} is empty. 9 | * 10 | * @export 11 | * @template A The value type. 12 | * @param {AsyncStream} fa The input stream. 13 | * @return {Task>>} A task of an option of an async stream 14 | * whose first element is excluded. 15 | * 16 | * @__PURE__ 17 | */ 18 | export function tail(fa: AsyncStream): Task>> { 19 | return async function _tail() { 20 | const gen = fa() 21 | const { done } = await gen.next() 22 | 23 | if (done) return none 24 | return some(() => gen) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Stream/utils/append-all.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from '../uri' 2 | 3 | /** 4 | * Appends an element to every member of a {@link Stream}. 5 | * 6 | * @export 7 | * @template A The value type. 8 | * @param {A} middle The element to append. 9 | * @return {(fa: Stream) => Stream} A function that takes a stream 10 | * to modify. 11 | * 12 | * @__PURE__ 13 | */ 14 | export function appendAll(middle: A) { 15 | /** 16 | * Takes a {@link Stream} to append the previously given value. 17 | * 18 | * @param {Stream} fa The input stream. 19 | * @return {Stream} The output stream. 20 | * 21 | * @step 1 22 | * @__PURE__ 23 | */ 24 | return function _appendAll(fa: Stream): Stream { 25 | return function* __appendAll() { 26 | for (const a of fa()) { 27 | yield a 28 | yield middle 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/AsyncStream/transformations/from-io.ts: -------------------------------------------------------------------------------- 1 | import { FromIO1, fromIOK as fromIOFromIOK } from 'fp-ts/lib/FromIO' 2 | import { IO } from 'fp-ts/lib/IO' 3 | 4 | import { AsyncStream, URI } from '../uri' 5 | 6 | /** 7 | * Creates an {@link AsyncStream} from an {@link IO} instance. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @param {IO} fa The io instance. 12 | * @return {AsyncStream} The async stream output. 13 | * 14 | * @category conversions 15 | * @__PURE__ 16 | */ 17 | export function fromIO(fa: IO): AsyncStream { 18 | return async function* _fromIO() { 19 | yield fa() 20 | } 21 | } 22 | 23 | /** 24 | * The `FromIO` category instance for {@link AsyncStream}. 25 | */ 26 | export const FromIO: FromIO1 = { 27 | URI, 28 | fromIO 29 | } 30 | 31 | /** 32 | * @category lifting 33 | */ 34 | export const fromIOK = fromIOFromIOK(FromIO) 35 | -------------------------------------------------------------------------------- /src/Stream/utils/prepend-all.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from '../uri' 2 | 3 | /** 4 | * Prepends an element to every member of a {@link Stream}. 5 | * 6 | * @export 7 | * @template A The value type. 8 | * @param {A} middle The element to prepend. 9 | * @return {(fa: Stream) => Stream} A function that takes a stream 10 | * to modify. 11 | * 12 | * @__PURE__ 13 | */ 14 | export function prependAll(middle: A) { 15 | /** 16 | * Takes a {@link Stream} to prepend the previously given value. 17 | * 18 | * @param {Stream} fa The input stream. 19 | * @return {Stream} The output stream. 20 | * 21 | * @step 1 22 | * @__PURE__ 23 | */ 24 | return function _prependAll(fa: Stream): Stream { 25 | return function* __prependAll() { 26 | for (const a of fa()) { 27 | yield middle 28 | yield a 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Stream/zero.ts: -------------------------------------------------------------------------------- 1 | import { guard as zeroGuard, Zero1 } from 'fp-ts/lib/Zero' 2 | 3 | import { Pointed } from './pointed' 4 | import { Stream, URI } from './uri' 5 | 6 | /** 7 | * Returns an empty {@link Stream} of type `A`. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @return {Stream} A {@link Stream} instance that will yield no values. 12 | * 13 | * @category model 14 | * @__PURE__ 15 | */ 16 | export function zero(): Stream { 17 | return function* _zero() {} 18 | } 19 | 20 | /** 21 | * An empty {@link Stream} instance. 22 | */ 23 | export const empty = zero() 24 | 25 | /** 26 | * The `Zero` category instance for {@link Stream}. 27 | * 28 | * @category model 29 | */ 30 | export const Zero: Zero1 = { 31 | URI, 32 | zero, 33 | } 34 | 35 | /** 36 | * @category do notation 37 | */ 38 | export const guard = zeroGuard(Zero, Pointed) 39 | -------------------------------------------------------------------------------- /src/Stream/utils/find-first-map.ts: -------------------------------------------------------------------------------- 1 | import { flow } from 'fp-ts/lib/function' 2 | import { flatten, isSome, Option } from 'fp-ts/lib/Option' 3 | 4 | import { map } from '../functor' 5 | import { findFirst } from './find-first' 6 | 7 | /** 8 | * Given a selector function which takes an element and returns 9 | * an {@link Option}, this function applies the selector to each element of 10 | * the array and returns the first `Some` result. Otherwise it returns `None`. 11 | * 12 | * @export 13 | * @template A The value type. 14 | * @template B The mapped value type. 15 | * @param {(a: A) => Option} f The mapping function. 16 | * @return {(fa: Stream) => Option} A function that takes a stream and 17 | * returns an option of `B`. 18 | * 19 | * @__PURE__ 20 | */ 21 | export function findFirstMap(f: (a: A) => Option) { 22 | return flow(map(f), findFirst(isSome), flatten) 23 | } 24 | -------------------------------------------------------------------------------- /fixup.sh: -------------------------------------------------------------------------------- 1 | cat >dist/cjs/package.json <dist/esm/package.json <dist/cjs/Stream/package.json <dist/cjs/AsyncStream/package.json <dist/esm/Stream/package.json <dist/esm/AsyncStream/package.json <} fa The stream source. 9 | * @return {A[]} The stream items as an array. 10 | * 11 | * @__PURE__ 12 | */ 13 | export function toArray(fa: Stream): A[] { 14 | return Array.from(fa()) 15 | } 16 | 17 | /** 18 | * Converts an iterable to a {@link Stream} of type `A`. 19 | * 20 | * @export 21 | * @template A The value type. 22 | * @param {Iterable} a The iterable input of `A` values. 23 | * @return {Stream} A stream of of the values found in the given iterable. 24 | * 25 | * @__PURE__ 26 | */ 27 | export function fromIterable(a: Iterable): Stream { 28 | return function* _fromIterable() { 29 | for (const item of a) { 30 | yield item 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tsconfig-base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2015", 4 | "experimentalDecorators": true, 5 | "emitDecoratorMetadata": true, 6 | "module": "commonjs", 7 | "rootDir": "src", 8 | "moduleResolution": "node", 9 | "baseUrl": "src", 10 | "importHelpers": true, 11 | "declaration": true, 12 | "noUnusedLocals": true, 13 | "noImplicitReturns": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "stripInternal": true, 16 | "emitDeclarationOnly": false, 17 | "sourceMap": true, 18 | "newLine": "lf", 19 | "lib": [ 20 | "DOM", 21 | "ESNext" 22 | ], 23 | "esModuleInterop": true, 24 | "forceConsistentCasingInFileNames": true, 25 | "strict": true, 26 | "skipDefaultLibCheck": true, 27 | "skipLibCheck": true 28 | }, 29 | "exclude": [ 30 | "node_modules", 31 | "tests", 32 | "dist" 33 | ], 34 | "include": [ 35 | "src" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /src/Stream/utils/find-last-map.ts: -------------------------------------------------------------------------------- 1 | import { flow } from 'fp-ts/lib/function' 2 | import { flatten, isSome, Option } from 'fp-ts/lib/Option' 3 | 4 | import { map } from '../functor' 5 | import { Stream } from '../uri' 6 | import { findLast } from './find-last' 7 | 8 | /** 9 | * Given a selector function which takes an element and returns 10 | * an {@link Option}, this function applies the selector to each element of 11 | * the array and returns the last `Some` result. Otherwise it returns `None`. 12 | * 13 | * @export 14 | * @template A The value type. 15 | * @template B The mapped value type. 16 | * @param {(a: A) => Option} f The mapping function. 17 | * @return {(fa: Stream) => Option} A function that takes a stream and 18 | * returns an option of `B`. 19 | * 20 | * @__PURE__ 21 | */ 22 | export function findLastMap(f: (a: A) => Option): (fa: Stream) => Option { 23 | return flow(map(f), findLast(isSome), flatten) 24 | } 25 | -------------------------------------------------------------------------------- /src/Stream/utils/init.ts: -------------------------------------------------------------------------------- 1 | import { none, Option, some } from 'fp-ts/lib/Option' 2 | 3 | import { Stream } from '../uri' 4 | 5 | /** 6 | * Gets all but the last element of a {@link Stream}, creating a new 7 | * {@link Stream}, or `None` if the stream was empty. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @param {Stream} fa The input stream. 12 | * @return {Option>} An option of output stream. 13 | * 14 | * @__PURE__ 15 | */ 16 | export function init(fa: Stream): Option> { 17 | const gen = fa() 18 | const { done, value } = gen.next() 19 | 20 | if (done) return none 21 | return some(function* _init() { 22 | let curr = gen.next() 23 | if (curr.done) return 24 | 25 | yield value 26 | 27 | let last = curr.value 28 | while (!curr.done) { 29 | curr = gen.next() 30 | if (curr.done) return 31 | 32 | yield last 33 | last = curr.value 34 | } 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/append-all.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from '../uri' 2 | 3 | /** 4 | * Appends an element to every member of a {@link AsyncStream}. 5 | * 6 | * @export 7 | * @template A The value type. 8 | * @param {A} middle The element to append. 9 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 10 | * async stream to modify. 11 | * 12 | * @__PURE__ 13 | */ 14 | export function appendAll(middle: A) { 15 | /** 16 | * Takes an {@link AsyncStream} to append the previously given value. 17 | * 18 | * @param {AsyncStream} fa The input async stream. 19 | * @return {AsyncStream} The output async stream. 20 | * 21 | * @step 1 22 | * @__PURE__ 23 | */ 24 | return function _appendAll(fa: AsyncStream): AsyncStream { 25 | return async function* __appendAll() { 26 | for await (const a of fa()) { 27 | yield a 28 | yield middle 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/prepend-all.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from '../uri' 2 | 3 | /** 4 | * Prepends an element to every member of an {@link AsyncStream}. 5 | * 6 | * @export 7 | * @template A The value type. 8 | * @param {A} middle The element to prepend. 9 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 10 | * async stream to modify. 11 | * 12 | * @__PURE__ 13 | */ 14 | export function prependAll(middle: A) { 15 | /** 16 | * Takes an {@link AsyncStream} to prepend the previously given value. 17 | * 18 | * @param {AsyncStream} fa The input async stream. 19 | * @return {AsyncStream} The output async stream. 20 | * 21 | * @step 1 22 | * @__PURE__ 23 | */ 24 | return function _prependAll(fa: AsyncStream): AsyncStream { 25 | return async function* __prependAll() { 26 | for await (const a of fa()) { 27 | yield middle 28 | yield a 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/AsyncStream/transformations/from-readable.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from '../uri' 2 | 3 | /** 4 | * Creates an {@link AsyncStream} from a {@link ReadableStream} instance. 5 | * 6 | * @export 7 | * @template A The value type. 8 | * @param {ReadableStream} fa The readable stream instance. 9 | * @return {AsyncStream} The async stream output. 10 | * 11 | * @category conversions 12 | * @__PURE__ 13 | */ 14 | export function fromReadableStream(fa: ReadableStream): AsyncStream { 15 | return async function* _fromReadableStream() { 16 | const reader = fa.getReader() 17 | while (true) { 18 | const { done, value } = await reader.read() 19 | if (done) { 20 | if (typeof value !== 'undefined') { 21 | yield value 22 | } 23 | 24 | return 25 | } 26 | 27 | if (typeof value !== 'undefined') { 28 | yield value 29 | } 30 | else { 31 | return 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/delay.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from '../uri' 2 | 3 | /** 4 | * Creates an {@link AsyncStream} that will complete after a time delay. 5 | * 6 | * @export 7 | * @param {number} millis The amount of time to delay in milliseconds. 8 | * @return {(ma: AsyncStream) => AsyncStream} A function that takes an 9 | * async stream to delay. 10 | * 11 | * @__PURE__ 12 | */ 13 | export function delay(millis: number) { 14 | /** 15 | * Takes an {@link AsyncStream} to delay as much as the previously given 16 | * milliseconds. 17 | * 18 | * @template A The value type. 19 | * @param {AsyncStream} ma The input async stream. 20 | * @return {AsyncStream} The output async stream. 21 | * 22 | * @step 1 23 | * @__PURE__ 24 | */ 25 | return function _delay(ma: AsyncStream): AsyncStream { 26 | return async function* __delay() { 27 | await new Promise(resolve => setTimeout(resolve, millis)) 28 | yield* ma() 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Stream/transformations/show.ts: -------------------------------------------------------------------------------- 1 | import { Show } from 'fp-ts/lib/Show' 2 | 3 | import { Stream } from '../uri' 4 | 5 | /** 6 | * Gets a {@link Show} instance for {@link Stream} of type `A`. 7 | * 8 | * @export 9 | * @template A The value type. 10 | * @param {Show} S The {@link Show} instance for type `A`. 11 | * @return {Show>} The {@link Show} instance for {@link Stream} 12 | * of type `A`. 13 | * 14 | * @category instances 15 | * @__PURE__ 16 | */ 17 | export function getShow(S: Show): Show> { 18 | return { 19 | show(ma) { 20 | let str = '{'; 21 | const m = ma() 22 | let step = m.next() 23 | 24 | if (step.done) { 25 | return str + '}' 26 | } 27 | else { 28 | str += ` ${S.show(step.value)}` 29 | } 30 | 31 | step = m.next() 32 | 33 | while (!step.done) { 34 | str += `, ${S.show(step.value)}` 35 | step = m.next() 36 | } 37 | 38 | return str + ' }' 39 | }, 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Stream/utils/scan-left.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from '../uri' 2 | 3 | /** 4 | * Same as `reduce` but it carries over the intermediate steps. 5 | * 6 | * @export 7 | * @template A The value type. 8 | * @template B The output value type. 9 | * @param {B} b The initial value. 10 | * @param {(b: B, a: A) => B} f The mapping function. 11 | * @return {(fa: Stream) => Stream} A function that takes a stream and 12 | * returns another. 13 | * 14 | * @__PURE__ 15 | */ 16 | export function scanLeft(b: B, f: (b: B, a: A) => B) { 17 | /** 18 | * Takes a {@link Stream} and `scan`s it from the beginning. 19 | * 20 | * @param {Stream} fa The input stream. 21 | * @return {Stream} The output stream. 22 | * 23 | * @step 1 24 | * @__PURE__ 25 | */ 26 | return function _scanLeft(fa: Stream): Stream { 27 | return function* __scanLeft() { 28 | yield b 29 | for (const a of fa()) { 30 | b = f(b, a) 31 | yield b 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Stream/utils/flap.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from '../uri' 2 | 3 | /** 4 | * Given an input a {@link Stream} of functions, `flap` returns a {@link Stream} 5 | * containing the results of applying each function to the given input. 6 | * 7 | * @export 8 | * @template A The value type. 9 | * @param {A} a The value. 10 | * @return {(fab: Stream<(a: A) => B>) => Stream} A function that takes a 11 | * stream of functions. 12 | * 13 | * @category mapping 14 | * @__PURE__ 15 | */ 16 | export function flap(a: A) { 17 | /** 18 | * Takes a {@link Stream} of functions to apply the previously given value 19 | * to them. 20 | * 21 | * @template B The return value type. 22 | * @param {Stream<(a: A) => B>} fab The function stream. 23 | * @return {Stream} The output stream. 24 | * 25 | * @step 1 26 | * @category mapping 27 | * @__PURE__ 28 | */ 29 | return function _flap(fab: Stream<(a: A) => B>): Stream { 30 | return function* __flap() { 31 | for (const f of fab()) { 32 | yield f(a) 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Stream/utils/delete-at.ts: -------------------------------------------------------------------------------- 1 | import { identity } from 'fp-ts/lib/function' 2 | 3 | import { Stream } from '../uri' 4 | 5 | /** 6 | * Removes/skips an element at given index. 7 | * 8 | * *Note:* Negative indices will have no effect. 9 | * 10 | * @export 11 | * @param {number} i The index to exclude from the stream. 12 | * @return {(fa: Stream) => Stream} A function that takes a stream to 13 | * remove the item at index. 14 | * 15 | * @__PURE__ 16 | */ 17 | export function deleteAt(i: number) { 18 | if (i < 0) return identity 19 | 20 | /** 21 | * Takes a {@link Stream} to remove the element at previously 22 | * given index. 23 | * 24 | * @template A The value type. 25 | * @param {Stream} fa The input stream. 26 | * @return {Stream} The output stream. 27 | * 28 | * @step 1 29 | * @__PURE__ 30 | */ 31 | return function _deleteAt(fa: Stream): Stream { 32 | return function* __deleteAt() { 33 | for (const a of fa()) { 34 | if (i-- === 0) continue 35 | yield a 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/is-empty.ts: -------------------------------------------------------------------------------- 1 | import { Task } from 'fp-ts/lib/Task' 2 | 3 | import { AsyncStream } from '../uri' 4 | 5 | /** 6 | * Tests whether an {@link AsyncStream} is empty. 7 | * 8 | * @export 9 | * @template A The value type. 10 | * @param {AsyncStream} fa The input async stream. 11 | * @return {Task} A task of `true` if the async stream was empty, 12 | * `false` otherwise. 13 | * 14 | * @__PURE__ 15 | */ 16 | export function isEmpty(fa: AsyncStream): Task { 17 | return async function _isEmpty() { 18 | const gen = fa() 19 | const { done } = await gen.next() 20 | return !!done 21 | } 22 | } 23 | 24 | /** 25 | * Tests whether a {@link Stream} is not empty. 26 | * 27 | * @export 28 | * @template A The value type. 29 | * @param {AsyncStream} ma The input async stream. 30 | * @return {Promise} A task of `true` if the stream was not empty, 31 | * `false` otherwise. 32 | * 33 | * @__PURE__ 34 | */ 35 | export function isNotEmpty(ma: AsyncStream) { 36 | return () => isEmpty(ma)().then(value => !value) 37 | } 38 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/scan-left.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from '../uri' 2 | 3 | /** 4 | * Same as `reduce` but it carries over the intermediate steps. 5 | * 6 | * @export 7 | * @template A The value type. 8 | * @template B The output value type. 9 | * @param {B} b The initial value. 10 | * @param {(b: B, a: A) => B | Promise} f The mapping function. 11 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 12 | * async stream and returns another. 13 | * 14 | * @__PURE__ 15 | */ 16 | export function scanLeft(b: B, f: (b: B, a: A) => B | Promise) { 17 | /** 18 | * Takes an {@link AsyncStream} and `scan`s it from the beginning. 19 | * 20 | * @param {AsyncStream} fa The input async stream. 21 | * @return {AsyncStream} The output async stream. 22 | * 23 | * @step 1 24 | * @__PURE__ 25 | */ 26 | return function _scanLeft(fa: AsyncStream): AsyncStream { 27 | return async function* __scanLeft() { 28 | yield b 29 | for await (const a of fa()) { 30 | b = await f(b, a) 31 | yield b 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Stream/utils/intersperse.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from '../uri' 2 | 3 | /** 4 | * Places an element in between members of a {@link Stream}. 5 | * 6 | * @export 7 | * @template A The value type. 8 | * @param {A} middle The middle value. 9 | * @return {(fa: Stream) => Stream} A function that takes a stream to 10 | * modify. 11 | * 12 | * @__PURE__ 13 | */ 14 | export function intersperse(middle: A) { 15 | /** 16 | * Takes a {@link Stream} to place the previously given element between the 17 | * members of it. 18 | * 19 | * @param {Stream} fa The input stream. 20 | * @return {Stream} The output stream. 21 | * 22 | * @step 1 23 | * @__PURE__ 24 | */ 25 | return function _intersperse(fa: Stream): Stream { 26 | return function* __intersperse() { 27 | const gen = fa() 28 | let curr = gen.next() 29 | 30 | if (curr.done) return 31 | yield curr.value 32 | 33 | while (true) { 34 | const curr = gen.next() 35 | if (curr.done) break 36 | yield middle 37 | yield curr.value 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Stream/utils/some.ts: -------------------------------------------------------------------------------- 1 | import { Predicate } from 'fp-ts/lib/Predicate' 2 | 3 | import { Stream } from '../uri' 4 | 5 | /** 6 | * Check if a predicate holds true for any {@link Stream} member. 7 | * 8 | * @export 9 | * @template A The value type. 10 | * @param {Predicate} predicate The predicate function. 11 | * @return {(fa: Stream) => boolean} A function that takes a stream and 12 | * returns a boolean. 13 | * 14 | * @__PURE__ 15 | */ 16 | export function some(predicate: Predicate) { 17 | /** 18 | * Takes a {@link Stream} to check if any element is passes the previously 19 | * given predicate. 20 | * 21 | * @param {Stream} fa The input stream. 22 | * @return {boolean} `true` if any of the stream members passes from the 23 | * predicate, `false` otherwise. 24 | * 25 | * @__PURE__ 26 | */ 27 | return function _some(fa: Stream) { 28 | for (const a of fa()) { 29 | if (predicate(a)) { 30 | return true 31 | } 32 | } 33 | 34 | return false 35 | } 36 | } 37 | 38 | /** 39 | * Alias for {@link some}. 40 | */ 41 | export const exists = some 42 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/find-last-map.ts: -------------------------------------------------------------------------------- 1 | import { flow } from 'fp-ts/lib/function' 2 | import { flatten, isSome, Option } from 'fp-ts/lib/Option' 3 | import { map as mapTask, Task } from 'fp-ts/lib/Task' 4 | 5 | import { map } from '../functor' 6 | import { AsyncStream } from '../uri' 7 | import { findLast } from './find-last' 8 | import { MaybeAsync } from './maybe-async' 9 | 10 | /** 11 | * Given a selector function which takes an element and returns 12 | * an {@link Option}, this function applies the selector to each element of 13 | * the array and returns the last `Some` result. Otherwise it returns `None`. 14 | * 15 | * @export 16 | * @template A The value type. 17 | * @template B The mapped value type. 18 | * @param {(a: A) => Option | Promise>} f The mapping function. 19 | * @return {(fa: AsyncStream) => Task>} A function that takes a 20 | * stream and returns an option of `B`. 21 | * 22 | * @__PURE__ 23 | */ 24 | export function findLastMap(f: (a: A) => MaybeAsync>): (fa: AsyncStream) => Task> { 25 | return flow(map(f), findLast(isSome), mapTask(flatten)) 26 | } 27 | -------------------------------------------------------------------------------- /src/AsyncStream/transformations/from-either.ts: -------------------------------------------------------------------------------- 1 | import { Either, isLeft } from 'fp-ts/lib/Either' 2 | import { 3 | FromEither1, 4 | fromEitherK as fromEitherFromEitherK, 5 | } from 'fp-ts/lib/FromEither' 6 | 7 | import { of } from '../pointed' 8 | import { AsyncStream, URI } from '../uri' 9 | import { empty } from '../zero' 10 | 11 | /** 12 | * Creates an {@link AsyncStream} from an {@link Either} instance. 13 | * 14 | * @export 15 | * @template E The left value type. 16 | * @template A The right value type. 17 | * @param {Either} fa The either instance. 18 | * @return {AsyncStream} The async stream output. 19 | * 20 | * @category conversions 21 | * @__PURE__ 22 | */ 23 | export function fromEither(fa: Either): AsyncStream { 24 | if (isLeft(fa)) { 25 | return empty 26 | } 27 | else { 28 | return of(fa.right) 29 | } 30 | } 31 | 32 | /** 33 | * The `FromEither` category instance for {@link AsyncStream}. 34 | */ 35 | export const FromEither: FromEither1 = { 36 | URI, 37 | fromEither, 38 | } 39 | 40 | /** 41 | * @category lifting 42 | */ 43 | export const fromEitherK = fromEitherFromEitherK(FromEither) 44 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/delete-at.ts: -------------------------------------------------------------------------------- 1 | import { identity } from 'fp-ts/lib/function' 2 | 3 | import { AsyncStream } from '../uri' 4 | 5 | /** 6 | * Removes/skips an element at given index. 7 | * 8 | * *Note:* Negative indices will have no effect. 9 | * 10 | * @export 11 | * @param {number} i The index to exclude from the async stream. 12 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 13 | * async stream to remove the item at index. 14 | * 15 | * @__PURE__ 16 | */ 17 | export function deleteAt(i: number) { 18 | if (i < 0) return identity 19 | 20 | /** 21 | * Takes an {@link AsyncStream} to remove the element at previously 22 | * given index. 23 | * 24 | * @template A The value type. 25 | * @param {AsyncStream} fa The input async stream. 26 | * @return {Stream} The output async stream. 27 | * 28 | * @step 1 29 | * @__PURE__ 30 | */ 31 | return function _deleteAt(fa: AsyncStream): AsyncStream { 32 | return async function* __deleteAt() { 33 | for await (const a of fa()) { 34 | if (i-- === 0) continue 35 | yield a 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/find-first-map.ts: -------------------------------------------------------------------------------- 1 | import { flow } from 'fp-ts/lib/function' 2 | import { flatten, isSome, Option } from 'fp-ts/lib/Option' 3 | import { map as mapTask, Task } from 'fp-ts/lib/Task' 4 | 5 | import { map } from '../functor' 6 | import { AsyncStream } from '../uri' 7 | import { findFirst } from './find-first' 8 | import { MaybeAsync } from './maybe-async' 9 | 10 | /** 11 | * Given a selector function which takes an element and returns 12 | * a {@link Task} of an {@link Option}, this function applies the selector to 13 | * each element of the array and returns the first `Some` result. 14 | * Otherwise it returns `None`. 15 | * 16 | * @export 17 | * @template A The value type. 18 | * @template B The mapped value type. 19 | * @param {(a: A) => Option | Promise>} f The mapping function. 20 | * @return {(fa: AsyncStream) => Task>} A function that takes an 21 | * async stream and returns an option of `B`. 22 | * 23 | * @__PURE__ 24 | */ 25 | export function findFirstMap(f: (a: A) => MaybeAsync>): (fa: AsyncStream) => Task> { 26 | return flow(map(f), findFirst(isSome), mapTask(flatten)) 27 | } 28 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/init.ts: -------------------------------------------------------------------------------- 1 | import { none, Option, some } from 'fp-ts/lib/Option' 2 | import { Task } from 'fp-ts/lib/Task' 3 | 4 | import { AsyncStream } from '../uri' 5 | 6 | /** 7 | * Gets all but the last element of an {@link AsyncStream}, creating a new 8 | * {@link AsyncStream}, or `None` if the stream was empty. 9 | * 10 | * @export 11 | * @template A The value type. 12 | * @param {AsyncStream} fa The input async stream. 13 | * @return {Task>>} A task of an option of output async 14 | * stream. 15 | * 16 | * @__PURE__ 17 | */ 18 | export function init(fa: AsyncStream): Task>> { 19 | return async function _init() { 20 | const gen = fa() 21 | const { done, value } = await gen.next() 22 | 23 | if (done) return none 24 | return some(async function* _init() { 25 | let curr = await gen.next() 26 | if (curr.done) return 27 | 28 | yield value 29 | 30 | let last = curr.value 31 | while (!curr.done) { 32 | curr = await gen.next() 33 | if (curr.done) return 34 | 35 | yield last 36 | last = curr.value 37 | } 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Stream/utils/uniq.ts: -------------------------------------------------------------------------------- 1 | import { Eq } from 'fp-ts/lib/Eq' 2 | 3 | import { Stream } from '../uri' 4 | 5 | /** 6 | * Removes the duplicates from a {@link Stream}, keeping the first occurence of 7 | * an element. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @param {Eq} E The equality instance. 12 | * @return {(fa: Stream) => Stream} A function that takes a stream to 13 | * keep its unique values. 14 | * 15 | * @__PURE__ 16 | */ 17 | export function uniq(E: Eq) { 18 | /** 19 | * Takes a {@link Stream} to keep its unique values. 20 | * 21 | * @param {Stream} fa The input stream. 22 | * @return {Stream} The output stream. 23 | * 24 | * @step 1 25 | * @__PURE__ 26 | */ 27 | return function _uniq(fa: Stream): Stream { 28 | return function* __uniq() { 29 | const as: A[] = [] 30 | 31 | outer: for (const a of fa()) { 32 | for (let i = 0, limit = as.length; i < limit; ++i) { 33 | const _a = as[ i ] 34 | if (E.equals(a, _a)) { 35 | continue outer 36 | } 37 | } 38 | 39 | yield a 40 | as.push(a) 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/intersperse.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from '../uri' 2 | 3 | /** 4 | * Places an element in between members of an {@link AsyncStream}. 5 | * 6 | * @export 7 | * @template A The value type. 8 | * @param {A} middle The middle value. 9 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 10 | * async stream to modify. 11 | * 12 | * @__PURE__ 13 | */ 14 | export function intersperse(middle: A) { 15 | /** 16 | * Takes an {@link AsyncStream} to place the previously given element between 17 | * the members of it. 18 | * 19 | * @param {AsyncStream} fa The input async stream. 20 | * @return {AsyncStream} The output async stream. 21 | * 22 | * @step 1 23 | * @__PURE__ 24 | */ 25 | return function _intersperse(fa: AsyncStream): AsyncStream { 26 | return async function* __intersperse() { 27 | const gen = fa() 28 | let curr = await gen.next() 29 | 30 | if (curr.done) return 31 | yield curr.value 32 | 33 | while (true) { 34 | const curr = await gen.next() 35 | if (curr.done) break 36 | yield middle 37 | yield curr.value 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Stream/utils/zip-with.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from '../uri' 2 | import { zip } from './zip' 3 | 4 | /** 5 | * Apply a function to pairs of elements at the same index in 6 | * two {@link Stream}s, collecting the results in a new {@link Stream}. 7 | * 8 | * If one input {@link Stream} is shorter, excess elements 9 | * of the other are discarded. 10 | * 11 | * @export 12 | * @template A The first value type. 13 | * @template B The second value type. 14 | * @template C The result value type. 15 | * @param {Stream} fa The first stream. 16 | * @param {Stream} fb The second stream. 17 | * @param {(a: A, b: B) => C} f The mapper function. 18 | * @return {Stream} The output stream. 19 | * 20 | * @__PURE__ 21 | */ 22 | export function zipWith(fa: Stream, fb: Stream, f: (a: A, b: B) => C): Stream { 23 | return function* _zipWith() { 24 | for (const ms of zip(fa, fb)()) { 25 | const itemGen = ms() 26 | 27 | let curr = itemGen.next() 28 | if (curr.done) return 29 | const a = curr.value as A 30 | 31 | curr = itemGen.next() 32 | if (curr.done) return 33 | const b = curr.value as B 34 | 35 | yield f(a, b) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/some.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from '../uri' 2 | import { AsyncPredicate } from './async-predicate' 3 | 4 | /** 5 | * Check if a predicate holds true for any {@link AsyncStream} member. 6 | * 7 | * @export 8 | * @template A The value type. 9 | * @param {Predicate} predicate The predicate function. 10 | * @return {(fa: AsyncStream) => Task} A function that takes an 11 | * async stream and returns a boolean. 12 | * 13 | * @__PURE__ 14 | */ 15 | export function some(predicate: AsyncPredicate) { 16 | /** 17 | * Takes an {@link AsyncStream} to check if any element is passes the 18 | * previously given predicate. 19 | * 20 | * @param {AsyncStream} fa The input stream. 21 | * @return {Task} `true` if any of the stream members passes from the 22 | * predicate, `false` otherwise. 23 | * 24 | * @__PURE__ 25 | */ 26 | return function _some(fa: AsyncStream) { 27 | return async function __some() { 28 | for await (const a of fa()) { 29 | if (await predicate(a)) { 30 | return true 31 | } 32 | } 33 | 34 | return false 35 | } 36 | } 37 | } 38 | 39 | /** 40 | * Alias for {@link some}. 41 | */ 42 | export const exists = some 43 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/uniq.ts: -------------------------------------------------------------------------------- 1 | import { Eq } from 'fp-ts/lib/Eq' 2 | 3 | import { AsyncStream } from '../uri' 4 | 5 | /** 6 | * Removes the duplicates from an {@link AsyncStream}, keeping the first 7 | * occurence of an element. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @param {Eq} E The equality instance. 12 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 13 | * asnyc stream to keep its unique values. 14 | * 15 | * @__PURE__ 16 | */ 17 | export function uniq(E: Eq) { 18 | /** 19 | * Takes an {@link AsyncStream} to keep its unique values. 20 | * 21 | * @param {AsyncStream} fa The input async stream. 22 | * @return {AsyncStream} The output async stream. 23 | * 24 | * @step 1 25 | * @__PURE__ 26 | */ 27 | return function _uniq(fa: AsyncStream): AsyncStream { 28 | return async function* __uniq() { 29 | const as: A[] = [] 30 | 31 | outer: for await (const a of fa()) { 32 | for (let i = 0, limit = as.length; i < limit; ++i) { 33 | const _a = as[ i ] 34 | if (E.equals(a, _a)) { 35 | continue outer 36 | } 37 | } 38 | 39 | yield a 40 | as.push(a) 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Stream/transformations/from-either.ts: -------------------------------------------------------------------------------- 1 | import { Either, isRight } from 'fp-ts/lib/Either' 2 | import { FromEither1, fromEitherK as fromEitherK_ } from 'fp-ts/lib/FromEither' 3 | 4 | import { of } from '../pointed' 5 | import { Stream, URI } from '../uri' 6 | import { empty } from '../zero' 7 | 8 | /** 9 | * Create a {@link Stream} from {@link Either}. 10 | * 11 | * The resulting {@link Stream} will contain the content of the {@link Either} 12 | * if it is `Right` and it will be empty if the {@link Either} is `Left. 13 | * 14 | * @export 15 | * @template A The value type. 16 | * @param {Either} fa The either input. 17 | * @return {Stream} The output stream. 18 | * 19 | * @category conversions 20 | * @__PURE__ 21 | */ 22 | export function fromEither(fa: Either): Stream { 23 | if (isRight(fa)) { 24 | return of(fa.right) 25 | } 26 | else { 27 | return empty 28 | } 29 | } 30 | 31 | /** 32 | * The `FromEither` category instance for {@link Stream}. 33 | * 34 | * @category conversions 35 | */ 36 | export const FromEither: FromEither1 = { 37 | URI, 38 | fromEither, 39 | } 40 | 41 | /** 42 | * @category lifting 43 | * 44 | * @__PURE__ 45 | */ 46 | export const fromEitherK = fromEitherK_(FromEither) 47 | -------------------------------------------------------------------------------- /src/AsyncStream/apply.ts: -------------------------------------------------------------------------------- 1 | import { 2 | apFirst as applyApFirst, 3 | Apply1, 4 | apS as applyApS, 5 | apSecond as applyApSecond, 6 | } from 'fp-ts/lib/Apply' 7 | 8 | import { Applicative } from './applicative' 9 | import { ApplicativeSeq } from './applicative-seq' 10 | import { Functor } from './functor' 11 | import { URI } from './uri' 12 | 13 | /** 14 | * The `Apply` category instance for {@link AsyncStream}. 15 | * 16 | * Same with {@link ApplyPar} 17 | */ 18 | export const Apply: Apply1 = { 19 | URI, 20 | ap: Applicative.ap, 21 | map: Functor.map, 22 | } 23 | 24 | /** 25 | * Combine two effectful actions, keeping only the result of the first. 26 | */ 27 | export const apFirst = applyApFirst(Apply) 28 | 29 | /** 30 | * Combine two effectful actions, keeping only the result of the second. 31 | */ 32 | export const apSecond = applyApSecond(Apply) 33 | 34 | /** 35 | * @category do notation 36 | */ 37 | export const apS = applyApS(Apply) 38 | 39 | /** 40 | * The `ApplySeq` category instance for {@link AsyncStream}. 41 | */ 42 | export const ApplySeq: Apply1 = { 43 | URI, 44 | ap: ApplicativeSeq.ap, 45 | map: Functor.map 46 | } 47 | 48 | /** 49 | * The `Apply` category instance for {@link AsyncStream}. 50 | */ 51 | export const ApplyPar = Apply 52 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/flap.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from '../uri' 2 | import { MaybeAsync } from './maybe-async' 3 | 4 | /** 5 | * Given an input an {@link AsyncStream} of functions, `flap` returns 6 | * an {@link AsyncStream} containing the results of applying each function 7 | * to the given input. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @param {A} a The value. 12 | * @return {(fab: AsyncStream<(a: A) => B | Promise>) => AsyncStream} A 13 | * function that takes an async stream of functions. 14 | * 15 | * @category mapping 16 | * @__PURE__ 17 | */ 18 | export function flap(a: A) { 19 | /** 20 | * Takes an {@link AsyncStream} of functions to apply the previously given 21 | * value to them. 22 | * 23 | * @template B The return value type. 24 | * @param {AsyncStream<(a: A) => B | Promise>} fab The function async 25 | * stream. 26 | * 27 | * @return {AsyncStream} The output async stream. 28 | * 29 | * @step 1 30 | * @category mapping 31 | * @__PURE__ 32 | */ 33 | return function _flap(fab: AsyncStream<(a: A) => MaybeAsync>): AsyncStream { 34 | return async function* __flap() { 35 | for await (const f of fab()) { 36 | yield await f(a) 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Stream/transformations/eq.ts: -------------------------------------------------------------------------------- 1 | import { Eq } from 'fp-ts/lib/Eq' 2 | 3 | import { Stream } from '../uri' 4 | 5 | /** 6 | * Derives on {@link Eq} over the {@link Stream} of a given element type from 7 | * the {@link Eq} of that type. 8 | * 9 | * The derived {@link Eq} defines two {@link Stream}s as equal if all elements 10 | * of both {@link Stream}s are compared equal pairwise with the given `E`. 11 | * 12 | * In case of {@link Stream}s of different lengths, the result is non equality. 13 | * 14 | * @export 15 | * @template A The value type. 16 | * @param {Eq} E The equality instance. 17 | * @return {Eq>} Another equality instance for the stream of 18 | * type `A`. 19 | * 20 | * @category instances 21 | * @__PURE__ 22 | */ 23 | export function getEq(E: Eq): Eq> { 24 | return { 25 | equals(x, y) { 26 | const xIter = x() 27 | const yIter = y() 28 | 29 | while (true) { 30 | const currX = xIter.next() 31 | const currY = yIter.next() 32 | 33 | if (currX.done !== currY.done) return false 34 | if (currX.done) return true 35 | const xVal = currX.value 36 | const yVal = currY.value 37 | 38 | if (!E.equals(xVal, yVal)) return false 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Stream/utils/modify-at.ts: -------------------------------------------------------------------------------- 1 | import { identity } from 'fp-ts/lib/function' 2 | 3 | import { Stream } from '../uri' 4 | 5 | /** 6 | * Applies a function to the element at the specified index, creating 7 | * a new {@link Stream}. 8 | * 9 | * *Note*: If the index was negative, the {@link Stream} will not be modified. 10 | * 11 | * @export 12 | * @template A The value type. 13 | * @param {number} i The index of the element to modify. 14 | * @param {(a: A) => A} f The function to modify the element. 15 | * @return {Stream} The stream whose value at given index is modified. 16 | * 17 | * @__PURE__ 18 | */ 19 | export function modifyAt(i: number, f: (a: A) => A) { 20 | if (i < 0) return identity 21 | 22 | /** 23 | * Takes a {@link Stream} to modify its element at previously given 24 | * index and returns a new {@link Stream}. 25 | * 26 | * @param {Stream} fa The input stream. 27 | * @return {Stream} The output stream. 28 | * 29 | * @step 1 30 | * @__PURE__ 31 | */ 32 | return function _modifyAt(fa: Stream): Stream { 33 | return function* __modifyAt() { 34 | for (const a of fa()) { 35 | if (i-- === 0) { 36 | yield f(a) 37 | continue 38 | } 39 | 40 | yield a 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/AsyncStream/transformations/monoid.ts: -------------------------------------------------------------------------------- 1 | import { Eq } from 'fp-ts/lib/Eq' 2 | import { Monoid } from 'fp-ts/lib/Monoid' 3 | 4 | import { concat } from '../concat' 5 | import { AsyncStream } from '../uri' 6 | import { empty } from '../zero' 7 | import { getUnionSemigroup } from './semigroup' 8 | 9 | /** 10 | * Returns a {@link Monoid} for {@link Stream}. 11 | * 12 | * @export 13 | * @template A The value type. 14 | * @return {Monoid>} A {@link Monoid} instance for 15 | * {@link AsyncStream} of type `A`. 16 | * 17 | * @category instances 18 | * @__PURE__ 19 | */ 20 | export function getMonoid(): Monoid> { 21 | return { 22 | empty, 23 | concat: (x, y) => concat(y)(x) 24 | } 25 | } 26 | 27 | /** 28 | * Returns a {@link Monoid} for {@link AsyncStream} which contains the union 29 | * of the elements. 30 | * 31 | * @export 32 | * @template A The value type. 33 | * @param {Eq} E The {@link Eq} instance for type `A`. 34 | * @return {Monoid>} A {@link Monoid} instance for 35 | * {@link AsyncStream} of type `A`. 36 | * 37 | * @category instances 38 | * @__PURE__ 39 | */ 40 | export function getUnionMonoid(E: Eq): Monoid> { 41 | return { 42 | empty, 43 | concat: getUnionSemigroup(E).concat, 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Stream/utils/prepend.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from '../uri' 2 | 3 | /** 4 | * Less strict version of {@link prepend}. 5 | * 6 | * @export 7 | * @template B The value type. 8 | * @param {B} head The value to prepend to the stream. 9 | * @return {(fa: Stream) => Stream} A function that takes a stream to 10 | * modify. 11 | * 12 | * @__PURE__ 13 | */ 14 | export function prependW(head: B) { 15 | /** 16 | * Takes a {@link Stream} to prepend the previously given value. 17 | * 18 | * @template A The value type. 19 | * @param {Stream} fa The input stream. 20 | * @return {(Stream)} The output stream. 21 | * 22 | * @step 1 23 | * @__PURE__ 24 | */ 25 | return function _prepend(fa: Stream): Stream { 26 | return function* __prepend() { 27 | yield head 28 | yield* fa() 29 | } 30 | } 31 | } 32 | 33 | /** 34 | * Prepends an element to the front of the {@link Stream}, creating a new 35 | * {@link Stream}. 36 | * 37 | * @export 38 | * @template A The value type. 39 | * @param {A} head The value to prepend to the stream. 40 | * @return {(fa: Stream) => Stream} A function that takes a stream to 41 | * modify. 42 | * 43 | * @__PURE__ 44 | */ 45 | export function prepend(head: A) { 46 | return prependW(head) 47 | } 48 | -------------------------------------------------------------------------------- /src/Stream/utils/split-at.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from 'fp-ts/lib/function' 2 | 3 | import { Stream } from '../uri' 4 | import { empty } from '../zero' 5 | import { dropLeft } from './drop-left' 6 | import { takeLeft } from './take-left' 7 | 8 | /** 9 | * Splits a {@link Stream} into two pieces, the first piece has max `n` 10 | * elements. 11 | * 12 | * @export 13 | * @param {number} n The number of elements the first stream will contain. 14 | * @return {(fa: Stream) => Stream>} A function that takes a stream 15 | * to split. 16 | * 17 | * @__PURE__ 18 | */ 19 | export function splitAt(n: number) { 20 | /** 21 | * Takes a {@link Stream} to split at previously given index. 22 | * 23 | * @template A The value type. 24 | * @param {Stream} fa The input stream. 25 | * @return {Stream>} A new stream of two streams. 26 | * 27 | * @step 1 28 | * @__PURE__ 29 | */ 30 | return function _splitAt(fa: Stream): Stream> { 31 | return function* __splitAt() { 32 | if (n <= 0) { 33 | yield empty 34 | yield fa 35 | } 36 | else { 37 | yield pipe( 38 | fa, 39 | takeLeft(n) 40 | ) 41 | 42 | yield pipe( 43 | fa, 44 | dropLeft(n) 45 | ) 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/zip-with.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from '../uri' 2 | import { zip } from './zip' 3 | 4 | /** 5 | * Apply a function to pairs of elements at the same index in 6 | * two {@link AsyncStream}s, collecting the results in a 7 | * new {@link AsyncStream}. 8 | * 9 | * If one input {@link AsyncStream} is shorter, excess elements 10 | * of the other are discarded. 11 | * 12 | * @export 13 | * @template A The first value type. 14 | * @template B The second value type. 15 | * @template C The result value type. 16 | * @param {AsyncStream} fa The first async stream. 17 | * @param {AsyncStream} fb The second async stream. 18 | * @param {(a: A, b: B) => C | Promise} f The mapper function. 19 | * @return {AsyncStream} The output async stream. 20 | * 21 | * @__PURE__ 22 | */ 23 | export function zipWith(fa: AsyncStream, fb: AsyncStream, f: (a: A, b: B) => C | Promise): AsyncStream { 24 | return async function* _zipWith() { 25 | for await (const ms of zip(fa, fb)()) { 26 | const itemGen = ms() 27 | 28 | let curr = await itemGen.next() 29 | if (curr.done) return 30 | const a = curr.value as A 31 | 32 | curr = await itemGen.next() 33 | if (curr.done) return 34 | const b = curr.value as B 35 | 36 | yield await f(a, b) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Stream/utils/append.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from '../uri' 2 | 3 | /** 4 | * Append an element to the end of the {@link Stream}, creating a new 5 | * {@link Stream}. 6 | * 7 | * @export 8 | * @template A The value type. 9 | * @param {A} end The value that will be added to the stream. 10 | * @return {Stream} Another stream that provides the given value at the end. 11 | * 12 | * @__PURE__ 13 | */ 14 | export function append(end: A) { 15 | return /**#__PURE__ */ appendW(end) 16 | } 17 | 18 | /** 19 | * Less strict version of {@link append}. 20 | * 21 | * @export 22 | * @template B The input value type. 23 | * @param {B} end The element that will be added to the end of the stream. 24 | * @return {Stream} Another stream that provides the given value at 25 | * the end. 26 | * 27 | * @__PURE__ 28 | */ 29 | export function appendW(end: B) { 30 | /** 31 | * Takes a {@link Stream} to add the previously given input to the end 32 | * of it. 33 | * 34 | * @template A The value type. 35 | * @param {Stream} fa The input stream. 36 | * @return {(Stream)} The output stream. 37 | * 38 | * @step 1 39 | * @__PURE__ 40 | */ 41 | return function _append(fa: Stream): Stream { 42 | return function* __append() { 43 | yield* fa() 44 | yield end 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Stream/unfoldable.ts: -------------------------------------------------------------------------------- 1 | import { isSome, Option } from 'fp-ts/lib/Option' 2 | import { Unfoldable1 } from 'fp-ts/lib/Unfoldable' 3 | 4 | import { Stream, URI } from './uri' 5 | 6 | /** 7 | * Takes a function which returns an {@link Option} of a tuple containing an 8 | * outcome value and an input for the following iteration. 9 | * 10 | * This function applies given `f` to the inital value `b` and then recursively 11 | * to the second element of the tuple contained in the returned {@link Option} 12 | * of the previous calculation until `f` returns `None`. 13 | * 14 | * @export 15 | * @template A The value type. 16 | * @template B The initial _seed_ value type. 17 | * @param {B} b The initial value. 18 | * @param {(b: B) => Option} f A function that recursively 19 | * provides value-seed tuple for the output stream. 20 | * 21 | * @returns {Stream} The output stream. 22 | * @__PURE__ 23 | */ 24 | export function unfold(b: B, f: (b: B) => Option): Stream { 25 | return function* _unfold() { 26 | for (let current = f(b); isSome(current); current = f(current.value[ 1 ])) { 27 | yield current.value[ 0 ] 28 | } 29 | } 30 | } 31 | 32 | /** 33 | * The `Unfoldable` category instance for {@link Stream}. 34 | */ 35 | export const Unfoldable: Unfoldable1 = { 36 | URI, 37 | unfold, 38 | } 39 | -------------------------------------------------------------------------------- /src/Stream/utils/count-by.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from 'fp-ts/lib/function' 2 | 3 | import { reduce } from '../foldable' 4 | 5 | import type { Stream } from '../uri' 6 | 7 | /** 8 | * Map each item of a {@link Stream} to a key and count how mony map to 9 | * each key. 10 | * 11 | * @export 12 | * @template A The type of the item. 13 | * @param {(x: A) => string} f The key from value function. 14 | * @return {(input: Stream) => Record} A function that will 15 | * take a {@link Stream} and returns the counts of its mapped values. 16 | * 17 | * @__PURE__ 18 | */ 19 | export function countBy(f: (x: A) => string) { 20 | /** 21 | * Map each item of a {@link Stream} to a key and count how mony map to 22 | * each key. 23 | * 24 | * @step 1 25 | * @template A The type of the item. 26 | * @param {Stream} input The input stream to count its values. 27 | * @return {Record} A record containing the mapped values 28 | * of the stream and their values. 29 | * 30 | * @__PURE__ 31 | */ 32 | return function _countBy(input: Stream): Record { 33 | return pipe( 34 | input, 35 | reduce({} as Record, (rec, a) => { 36 | const key = f(a) 37 | rec[ key ] ||= 0 38 | rec[ key ] += 1 39 | return rec 40 | }) 41 | ) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Stream/utils/chunks-of.ts: -------------------------------------------------------------------------------- 1 | import { of } from '../pointed' 2 | import { Stream } from '../uri' 3 | 4 | /** 5 | * Splits a {@link Stream} into {@link Stream} of {@link Streams} of given 6 | * chunk size. 7 | * 8 | * @export 9 | * @param {number} n The chunk size. 10 | * @return {(fa: Stream) => Stream>} A function that takes a 11 | * stream and returns a stream of streams of given chunks. 12 | * 13 | * @__PURE__ 14 | */ 15 | export function chunksOf(n: number) { 16 | /** 17 | * Takes a {@link Stream} to produce chunks of previously given number. 18 | * 19 | * @template A The value type. 20 | * @param {Stream} fa The input stream. 21 | * @return {Stream>} The output stream. 22 | * 23 | * @__PURE__ 24 | */ 25 | return function _chunksOf(fa: Stream): Stream> { 26 | if (n <= 0) return of(fa) 27 | 28 | return function* __chunksOf() { 29 | const genA = fa() 30 | 31 | let willBreak = false 32 | while (!willBreak) { 33 | yield function* ___chunksOf() { 34 | for (let i = n; i > 0; --i) { 35 | const { value, done } = genA.next() 36 | 37 | if (done) { 38 | willBreak = true 39 | return 40 | } 41 | 42 | yield value 43 | } 44 | } 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Stream/concat.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from './uri' 2 | 3 | /** 4 | * Concatenates two {@link Stream}s into one. 5 | * 6 | * @export 7 | * @template A The value type. 8 | * @param {Stream} second The another stream to concat. 9 | * @return {(first: Stream) => Stream} A function that will take the 10 | * initial stream. 11 | * 12 | * @__PURE__ 13 | */ 14 | export function concat(second: Stream) { 15 | return /**#__PURE__ */ concatW(second) 16 | } 17 | 18 | /** 19 | * Less strict version of {@link concat}. 20 | * 21 | * @export 22 | * @template B The value type of the another stream. 23 | * @param {Stream} second The another stream to concat. 24 | * @return {(first: Stream) => Stream} A function that will take 25 | * the initial stream. 26 | * 27 | * @__PURE__ 28 | */ 29 | export function concatW(second: Stream) { 30 | /** 31 | * Takes a {@link Stream} to concat the previously given {@link Stream} to 32 | * it. 33 | * 34 | * @template A The value type. 35 | * @param {Stream} first The initial stream. 36 | * @return {(Stream)} The output stream. 37 | * 38 | * @step 1 39 | * @__PURE__ 40 | */ 41 | return function _concatW(first: Stream): Stream { 42 | return function* __concatW() { 43 | yield* first() 44 | yield* second() 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Stream/utils/none.ts: -------------------------------------------------------------------------------- 1 | import { not, Predicate } from 'fp-ts/lib/Predicate' 2 | import { Refinement } from 'fp-ts/lib/Refinement' 3 | 4 | import { Stream } from '../uri' 5 | import { every } from './every' 6 | 7 | /** 8 | * `none` tells if the provided refinement holds `false` for every element 9 | * in the {@link Stream}. 10 | * 11 | * @export 12 | * @template A The value type. 13 | * @template B The refined value type. 14 | * @param {Refinement} refinement The refinement function. 15 | * @return {Refinement, Stream>} `true` if all the elements 16 | * returned `false` from the refinement function. 17 | * 18 | * @__PURE__ 19 | */ 20 | export function none( 21 | refinement: Refinement 22 | ): Refinement, Stream> 23 | 24 | /** 25 | * `none` tells if the provided predicate holds false for none element 26 | * in the {@link Stream}. 27 | * 28 | * @export 29 | * @template A The value type. 30 | * @template B The refined value type. 31 | * @param {Predicate} predicate The predicate function. 32 | * @return {Predicate>} `true` if all the elements 33 | * returned `false` from the refinement function. 34 | * 35 | * @__PURE__ 36 | */ 37 | export function none(predicate: Predicate): Predicate> 38 | export function none(predicate: Predicate) { 39 | return every(not(predicate)) 40 | } 41 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/modify-at.ts: -------------------------------------------------------------------------------- 1 | import { identity } from 'fp-ts/lib/function' 2 | 3 | import { AsyncStream } from '../uri' 4 | 5 | /** 6 | * Applies a function to the element at the specified index, creating 7 | * a new {@link AsyncStream}. 8 | * 9 | * *Note*: If the index was negative, the {@link AsyncStream} will not be 10 | * modified. 11 | * 12 | * @export 13 | * @template A The value type. 14 | * @param {number} i The index of the element to modify. 15 | * @param {(a: A) => A | Promise} f The function to modify the element. 16 | * @return {AsyncStream} The stream whose value at given index is modified. 17 | * 18 | * @__PURE__ 19 | */ 20 | export function modifyAt(i: number, f: (a: A) => A | Promise) { 21 | if (i < 0) return identity 22 | 23 | /** 24 | * Takes an {@link AsyncStream} to modify its element at previously given 25 | * index and returns a new {@link AsyncStream}. 26 | * 27 | * @param {AsyncStream} fa The input async stream. 28 | * @return {AsyncStream} The output async stream. 29 | * 30 | * @step 1 31 | * @__PURE__ 32 | */ 33 | return function _modifyAt(fa: AsyncStream): AsyncStream { 34 | return async function* __modifyAt() { 35 | for await (const a of fa()) { 36 | if (i-- === 0) { 37 | yield await f(a) 38 | continue 39 | } 40 | 41 | yield a 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/prepend.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from '../uri' 2 | 3 | /** 4 | * Less strict version of {@link prepend}. 5 | * 6 | * @export 7 | * @template B The value type. 8 | * @param {B} head The value to prepend to the async stream. 9 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 10 | * async stream to modify. 11 | * 12 | * @__PURE__ 13 | */ 14 | export function prependW(head: B) { 15 | /** 16 | * Takes an {@link AsyncStream} to prepend the previously given value. 17 | * 18 | * @template A The value type. 19 | * @param {AsyncStream} fa The input async stream. 20 | * @return {(AsyncStream)} The output async stream. 21 | * 22 | * @step 1 23 | * @__PURE__ 24 | */ 25 | return function _prepend(fa: AsyncStream): AsyncStream { 26 | return async function* __prepend() { 27 | yield head 28 | yield* fa() 29 | } 30 | } 31 | } 32 | 33 | /** 34 | * Prepends an element to the front of the {@link AsyncStream}, creating a new 35 | * {@link AsyncStream}. 36 | * 37 | * @export 38 | * @template A The value type. 39 | * @param {A} head The value to prepend to the async stream. 40 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 41 | * async stream to modify. 42 | * 43 | * @__PURE__ 44 | */ 45 | export function prepend(head: A) { 46 | return prependW(head) 47 | } 48 | -------------------------------------------------------------------------------- /src/Stream/utils/take-right.ts: -------------------------------------------------------------------------------- 1 | import { toArray } from '../conversions' 2 | import { Stream } from '../uri' 3 | 4 | /** 5 | * Takes given amount of items from a {@link Stream} from the end to the start. 6 | * 7 | * If negative value is passed, an empty {@link Stream} will be returned. 8 | * 9 | * **Warning: This function consumes the stream.** 10 | * 11 | * @export 12 | * @param {number} n The number of elements to take. 13 | * @return {(fa: Stream) => Stream} A function that takes a stream and 14 | * returns another stream that contains only given amount of elements. 15 | * 16 | * @__PURE__ 17 | */ 18 | export function takeRight(n: number) { 19 | /** 20 | * Takes previously given amount of elements from the given {@link Stream} 21 | * from the end to the start and returns another one contains that much 22 | * amount of items. 23 | * 24 | * @template A The value type. 25 | * @param {Stream} fa The input stream. 26 | * @return {Stream} The output stream. 27 | * 28 | * @step 1 29 | * @__PURE__ 30 | */ 31 | return function _takeRight(fa: Stream): Stream { 32 | return function* __takeRight() { 33 | if (n <= 0) { return } 34 | 35 | const as = toArray(fa) 36 | for ( 37 | let limit = as.length, i = Math.max(limit - n, 0); 38 | n > 0 && i < limit; 39 | ++i, --n 40 | ) { yield as[ i ] } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Streams for `fp-ts` 2 | 3 | This library provides `Stream` and `AsyncStream` type classes which uses 4 | `Generator` and `AsyncGenerator` internally, therefore enabling the developers 5 | to have _transducers_, that are executed as much as they are used. See the 6 | following example: 7 | 8 | ```ts 9 | import { range, filter, map, take, toArray } from 'fp-ts-stream/Stream' 10 | import { pipe } from 'fp-ts/lib/function' 11 | 12 | // The following will only run/execute the `range` iterator only 4 times. 13 | // 14 | // No array will be allocated, until `toArray` and the rest of the iteration 15 | // will be skipped/not completed. 16 | pipe( 17 | range(0, 10000), // iterates 4 times 18 | filter(it => it % 2 == 1), // iterates 2 times 19 | map(it => it * 3), // ... 20 | take(2), // ... 21 | toArray, // executed once 22 | console.log 23 | ) 24 | 25 | ``` 26 | 27 | The `Stream` is implementing all the functions that an array would normally 28 | have, therefore the instances of it admit implementations such as `Traversable`, 29 | `Chain` and etc. 30 | 31 | --- 32 | # Support 33 | To support the project, you can send donations to following addresses: 34 | 35 | ```md 36 | - Bitcoin : bc1qtut2ss8udkr68p6k6axd0na6nhvngm5dqlyhtn 37 | - Bitcoin Cash: qzmmv43ztae0tfsjx8zf4wwnq3uk6k7zzgcfr9jruk 38 | - Ether : 0xf542BED91d0218D9c195286e660da2275EF8eC84 39 | ``` 40 | -------------------------------------------------------------------------------- /src/Stream/utils/scan-right.ts: -------------------------------------------------------------------------------- 1 | import { toArray } from '../conversions' 2 | import { Stream } from '../uri' 3 | 4 | /** 5 | * Fold a {@link Stream} from the right, keeping all intermediate results 6 | * instead of the final result. 7 | * 8 | * **Warning: This function consumes the stream.** 9 | * 10 | * @export 11 | * @template A The value type. 12 | * @template B The output value type. 13 | * @param {B} b The initial value. 14 | * @param {(b: B, a: A) => B} f The mapping function. 15 | * @return {(fa: Stream) => Stream} A function that takes a stream and 16 | * returns another. 17 | * 18 | * @__PURE__ 19 | */ 20 | export function scanRight(b: B, f: (b: B, a: A) => B) { 21 | /** 22 | * Takes a {@link Stream} and `scan`s it from the end. 23 | * 24 | * **Warning: This function consumes the stream.** 25 | * 26 | * @param {Stream} fa The input stream. 27 | * @return {Stream} The output stream. 28 | * 29 | * @step 1 30 | * @__PURE__ 31 | */ 32 | return function _scanRight(fa: Stream): Stream { 33 | return function* __scanRight() { 34 | const last = b 35 | const as = toArray(fa) 36 | const bs = new Array(as.length) 37 | for (let i = as.length - 1; i >= 0; --i) { 38 | const a = as[ i ] 39 | b = f(b, a) 40 | bs[ i ] = b 41 | } 42 | 43 | for (const b of bs) yield b 44 | yield last 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/append.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from '../uri' 2 | 3 | /** 4 | * Append an element to the end of the {@link AsyncStream}, creating a new 5 | * {@link AsyncStream}. 6 | * 7 | * @export 8 | * @template A The value type. 9 | * @param {A} end The value that will be added to the stream. 10 | * @return {AsyncStream} Another async stream that provides the given value 11 | * at the end. 12 | * 13 | * @__PURE__ 14 | */ 15 | export function append(end: A) { 16 | return /**#__PURE__ */ appendW(end) 17 | } 18 | 19 | /** 20 | * Less strict version of {@link append}. 21 | * 22 | * @export 23 | * @template B The input value type. 24 | * @param {B} end The element that will be added to the end of the stream. 25 | * @return {AsyncStream} Another async stream that provides the given 26 | * value at the end. 27 | * 28 | * @__PURE__ 29 | */ 30 | export function appendW(end: B) { 31 | /** 32 | * Takes an {@link AsyncStream} to add the previously given input to the end 33 | * of it. 34 | * 35 | * @template A The value type. 36 | * @param {AsyncStream} fa The input stream. 37 | * @return {(StreaAsyncStreamm)} The output stream. 38 | * 39 | * @step 1 40 | * @__PURE__ 41 | */ 42 | return function _append(fa: AsyncStream): AsyncStream { 43 | return async function* __append() { 44 | yield* fa() 45 | yield end 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Stream/utils/cartesian.ts: -------------------------------------------------------------------------------- 1 | import type { Stream } from '../uri' 2 | 3 | /** 4 | * Returns the {@link https://en.wikipedia.org/wiki/Cartesian_product Cartesian product} 5 | * of two {@link Stream}s. In other words, returns a {@link Stream} containing tuples of every 6 | * possible ordered combination of the two input {@link Stream}s. 7 | * 8 | * @export 9 | * @template A The type of the left hand side of the output tuple. 10 | * @param {Stream} left The left hand side stream. 11 | * @return {(right: Stream) => Stream<[ A, B ]>} A function 12 | * that will take another stream to return the cartesian product of these. 13 | * 14 | * @__PURE__ 15 | */ 16 | export function cartesian(left: Stream) { 17 | /** 18 | * Returns the {@link https://en.wikipedia.org/wiki/Cartesian_product Cartesian product} 19 | * of the previously given {@link Stream} and this {@link Stream}. 20 | * 21 | * @step 1 22 | * @template B The type of the right hand side of the output tuple. 23 | * @param {Stream} right The right hand side stream. 24 | * @return {Stream<[ A, B ]>} A {@link Stream} of all `[A, B]` pairs. 25 | * 26 | * @__PURE__ 27 | */ 28 | return function _cartesian(right: Stream): Stream<[ A, B ]> { 29 | return function* __cartesian() { 30 | for (const lhs of left()) { 31 | for (const rhs of right()) { 32 | yield [ lhs, rhs ] 33 | } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/AsyncStream/concat.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from './uri' 2 | 3 | /** 4 | * Concatenates two {@link AsyncStream}s into one. 5 | * 6 | * @export 7 | * @template A The value type. 8 | * @param {AsyncStream} second The another async stream to concat. 9 | * @return {(first: AsyncStream) => AsyncStream} A function that will 10 | * take the initial stream. 11 | * 12 | * @__PURE__ 13 | */ 14 | export function concat(second: AsyncStream) { 15 | return /**#__PURE__ */ concatW(second) 16 | } 17 | 18 | /** 19 | * Less strict version of {@link concat}. 20 | * 21 | * @export 22 | * @template B The value type of the another async stream. 23 | * @param {AsyncStream} second The another async stream to concat. 24 | * @return {(first: AsyncStream) => AsyncStream} A function that 25 | * will take the initial stream. 26 | * 27 | * @__PURE__ 28 | */ 29 | export function concatW(second: AsyncStream) { 30 | /** 31 | * Takes a {@link Stream} to concat the previously given {@link Stream} to 32 | * it. 33 | * 34 | * @template A The value type. 35 | * @param {Stream} first The initial async stream. 36 | * @return {(Stream)} The output async stream. 37 | * 38 | * @step 1 39 | * @__PURE__ 40 | */ 41 | return function _concatW(first: AsyncStream): AsyncStream { 42 | return async function* __concatW() { 43 | yield* first() 44 | yield* second() 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Stream/utils/drop-left.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from '../uri' 2 | 3 | /** 4 | * Drops/skips the given amount of items from a {@link Stream}. 5 | * 6 | * @export 7 | * @param {number} count The number of elements to drop. 8 | * @return {(fa: Stream) => Stream} A function that takes a stream and 9 | * returns another stream that skips the given amount of elements. 10 | * 11 | * @__PURE__ 12 | */ 13 | export function dropLeft(count: number) { 14 | /** 15 | * Skips the previously given amount of elements from the given {@link Stream} 16 | * and returns another one skips that much of amount items. 17 | * 18 | * @template A The value type. 19 | * @param {Stream} fa The input stream. 20 | * @return {Stream} the output stream. 21 | * 22 | * @step 1 23 | * @__PURE__ 24 | */ 25 | return function _dropLeft(fa: Stream): Stream { 26 | if (count === 0) return fa 27 | 28 | return function* __dropLeft() { 29 | const gen = fa() 30 | for (; !gen.next().done && count > 1; --count); 31 | yield* gen 32 | } 33 | } 34 | } 35 | 36 | /** 37 | * Drops/skips the given amount of items from a {@link Stream}. 38 | * 39 | * @export 40 | * @param {number} count The number of elements to drop. 41 | * @return {(fa: Stream) => Stream} A function that takes a stream and 42 | * returns another stream that skips the given amount of elements. 43 | * 44 | * @__PURE__ 45 | */ 46 | export const skip = dropLeft 47 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/count-by.ts: -------------------------------------------------------------------------------- 1 | import { Task } from 'fp-ts/lib/Task' 2 | 3 | import { MaybeAsync } from './maybe-async' 4 | 5 | import type { AsyncStream } from '../uri' 6 | /** 7 | * Map each item of a {@link AsyncStream} to a key and count how mony map to 8 | * each key. 9 | * 10 | * @export 11 | * @template A The type of the item. 12 | * @param {(x: A) => MaybeAsync} f The key from value function. 13 | * @return {(input: AsyncStream) => Record} A function that will 14 | * take a {@link AsyncStream} and returns the counts of its mapped values. 15 | * 16 | * @__PURE__ 17 | */ 18 | export function countBy(f: (x: A) => MaybeAsync) { 19 | /** 20 | * Map each item of a {@link AsyncStream} to a key and count how mony map to 21 | * each key. 22 | * 23 | * @step 1 24 | * @template A The type of the item. 25 | * @param {AsyncStream} input The input stream to count its values. 26 | * @return {Record} A record containing the mapped values 27 | * of the stream and their values. 28 | * 29 | * @__PURE__ 30 | */ 31 | return function _countBy(input: AsyncStream): Task> { 32 | return async function __countBy() { 33 | let rec: Record = {} 34 | 35 | for await (const a of input()) { 36 | const key = await f(a) 37 | rec[ key ] ||= 0 38 | rec[ key ] += 1 39 | } 40 | 41 | return rec 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Stream/utils/every.ts: -------------------------------------------------------------------------------- 1 | import { Predicate } from 'fp-ts/lib/Predicate' 2 | import { Refinement } from 'fp-ts/lib/Refinement' 3 | 4 | import { Stream } from '../uri' 5 | 6 | /** 7 | * `every` tells if the provided refinement holds true for every element 8 | * in the {@link Stream}. 9 | * 10 | * @export 11 | * @template A The value type. 12 | * @template B The refined value type. 13 | * @param {Refinement} refinement The refinement function. 14 | * @return {Refinement, Stream>} `true` if all the elements 15 | * returned `true` from the refinement function. 16 | * 17 | * @__PURE__ 18 | */ 19 | export function every( 20 | refinement: Refinement 21 | ): Refinement, Stream> 22 | 23 | /** 24 | * `every` tells if the provided predicate holds true for every element 25 | * in the {@link Stream}. 26 | * 27 | * @export 28 | * @template A The value type. 29 | * @template B The refined value type. 30 | * @param {Predicate} predicate The predicate function. 31 | * @return {Predicate>} `true` if all the elements 32 | * returned `true` from the refinement function. 33 | * 34 | * @__PURE__ 35 | */ 36 | export function every(predicate: Predicate): Predicate> 37 | export function every(predicate: Predicate): Predicate> { 38 | return function _every(ma) { 39 | for (const a of ma()) { 40 | if (!predicate(a)) return false 41 | } 42 | 43 | return true 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/take-right.ts: -------------------------------------------------------------------------------- 1 | import { toArray } from '../conversions' 2 | import { AsyncStream } from '../uri' 3 | 4 | /** 5 | * Takes given amount of items from an {@link AsyncStream} from the end to the 6 | * start. 7 | * 8 | * If negative value is passed, an empty {@link AsyncStream} will be returned. 9 | * 10 | * **Warning: This function consumes the stream.** 11 | * 12 | * @export 13 | * @param {number} n The number of elements to take. 14 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 15 | * async stream and returns another stream that contains only given amount of 16 | * elements. 17 | * 18 | * @__PURE__ 19 | */ 20 | export function takeRight(n: number) { 21 | /** 22 | * Takes previously given amount of elements from the given 23 | * {@link AsyncStream} from the end to the start and returns another one 24 | * contains that much amount of items. 25 | * 26 | * @template A The value type. 27 | * @param {AsyncStream} fa The input async stream. 28 | * @return {AsyncStream} The output async stream. 29 | * 30 | * @step 1 31 | * @__PURE__ 32 | */ 33 | return function _takeRight(fa: AsyncStream): AsyncStream { 34 | return async function* __takeRight() { 35 | if (n <= 0) { return } 36 | 37 | const as = await toArray(fa) 38 | for ( 39 | let limit = as.length, i = Math.max(limit - n, 0); 40 | n > 0 && i < limit; 41 | ++i, --n 42 | ) { yield as[ i ] } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Stream/utils/drop-right.ts: -------------------------------------------------------------------------------- 1 | import { identity } from 'fp-ts/lib/function' 2 | 3 | import { fromIterable, toArray } from '../conversions' 4 | import { Stream } from '../uri' 5 | import { take } from './take-left' 6 | 7 | /** 8 | * Drops/skips the given amount of items from a {@link Stream} from end to 9 | * the start. 10 | * 11 | * **Warning: This function consumes the stream.** 12 | * 13 | * - Negative values will be equal to {@link take} as much as the value. 14 | * - If `0` is passed, the stream itself will be returned. 15 | * 16 | * @export 17 | * @param {number} count The number of elements to drop. 18 | * @return {(fa: Stream) => Stream} A function that takes a stream and 19 | * returns another stream that skips the given amount of elements. 20 | * 21 | * @__PURE__ 22 | */ 23 | export function dropRight(count: number) { 24 | if (count < 0) return take(-count) 25 | else if (count == 0) return identity 26 | 27 | /** 28 | * Skips the previously given amount of elements from the given {@link Stream} 29 | * and returns another one skips that much of amount items. 30 | * 31 | * **Warning: This function consumes the stream.** 32 | * 33 | * @template A The value type. 34 | * @param {Stream} fa The input stream. 35 | * @return {Stream} the output stream. 36 | * 37 | * @step 1 38 | * @__PURE__ 39 | */ 40 | return function _dropRight(fa: Stream): Stream { 41 | const source = toArray(fa).slice(0, -count) 42 | return fromIterable(source) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/cartesian.ts: -------------------------------------------------------------------------------- 1 | import type { AsyncStream } from '../uri' 2 | 3 | /** 4 | * Returns the {@link https://en.wikipedia.org/wiki/Cartesian_product Cartesian product} 5 | * of two {@link AsyncStream}s. In other words, returns a {@link AsyncStream} containing tuples of every 6 | * possible ordered combination of the two input {@link AsyncStream}s. 7 | * 8 | * @export 9 | * @template A The type of the left hand side of the output tuple. 10 | * @param {AsyncStream} left The left hand side stream. 11 | * @return {(right: AsyncStream) => AsyncStream<[ A, B ]>} A function 12 | * that will take another stream to return the cartesian product of these. 13 | * 14 | * @__PURE__ 15 | */ 16 | export function cartesian(left: AsyncStream) { 17 | /** 18 | * Returns the {@link https://en.wikipedia.org/wiki/Cartesian_product Cartesian product} 19 | * of the previously given {@link AsyncStream} and this {@link AsyncStream}. 20 | * 21 | * @step 1 22 | * @template B The type of the right hand side of the output tuple. 23 | * @param {AsyncStream} right The right hand side stream. 24 | * @return {AsyncStream<[ A, B ]>} A {@link AsyncStream} of all `[A, B]` pairs. 25 | * 26 | * @__PURE__ 27 | */ 28 | return function _cartesian(right: AsyncStream): AsyncStream<[ A, B ]> { 29 | return async function* __cartesian() { 30 | for await (const lhs of left()) { 31 | for await (const rhs of right()) { 32 | yield [ lhs, rhs ] 33 | } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/scan-right.ts: -------------------------------------------------------------------------------- 1 | import { toArray } from '../conversions' 2 | import { AsyncStream } from '../uri' 3 | 4 | /** 5 | * Fold an {@link AsyncStream} from the right, keeping all intermediate results 6 | * instead of the final result. 7 | * 8 | * **Warning: This function consumes the stream.** 9 | * 10 | * @export 11 | * @template A The value type. 12 | * @template B The output value type. 13 | * @param {B} b The initial value. 14 | * @param {(b: B, a: A) => B | Promise} f The mapping function. 15 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 16 | * async stream and returns another. 17 | * 18 | * @__PURE__ 19 | */ 20 | export function scanRight(b: B, f: (b: B, a: A) => B | Promise) { 21 | /** 22 | * Takes an {@link AsyncStream} and `scan`s it from the end. 23 | * 24 | * **Warning: This function consumes the stream.** 25 | * 26 | * @param {AsyncStream} fa The input async stream. 27 | * @return {AsyncStream} The output async stream. 28 | * 29 | * @step 1 30 | * @__PURE__ 31 | */ 32 | return function _scanRight(fa: AsyncStream): AsyncStream { 33 | return async function* __scanRight() { 34 | const last = b 35 | const as = await toArray(fa) 36 | const bs = new Array(as.length) 37 | for (let i = as.length - 1; i >= 0; --i) { 38 | const a = as[ i ] 39 | b = await f(b, a) 40 | bs[ i ] = b 41 | } 42 | 43 | for (const b of bs) yield b 44 | yield last 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/AsyncStream/unfoldable.ts: -------------------------------------------------------------------------------- 1 | import { isSome, Option } from 'fp-ts/lib/Option' 2 | import { Unfoldable1 } from 'fp-ts/lib/Unfoldable' 3 | 4 | import { AsyncStream, URI } from './uri' 5 | import { MaybeAsync } from './utils/maybe-async' 6 | 7 | /** 8 | * Takes a function which returns an {@link Option} of a tuple containing an 9 | * outcome value and an input for the following iteration. 10 | * 11 | * This function applies given `f` to the inital value `b` and then recursively 12 | * to the second element of the tuple contained in the returned {@link Option} 13 | * of the previous calculation until `f` returns `None`. 14 | * 15 | * @export 16 | * @template A The value type. 17 | * @template B The initial _seed_ value type. 18 | * @param {B} b The initial value. 19 | * @param {(b: B) => Option | Promise>} f A 20 | * function that recursively provides value-seed tuple for the output async 21 | * stream. 22 | * 23 | * @returns {AsyncStream} The output async stream. 24 | * @__PURE__ 25 | */ 26 | export function unfold(b: B, f: (b: B) => MaybeAsync>): AsyncStream { 27 | return async function* _unfold() { 28 | for ( 29 | let current = await f(b); 30 | isSome(current); 31 | current = await f(current.value[ 1 ]) 32 | ) { yield current.value[ 0 ] } 33 | } 34 | } 35 | 36 | /** 37 | * The `Unfoldable` category instance for {@link AsyncStream}. 38 | */ 39 | export const Unfoldable: Unfoldable1 = { 40 | URI, 41 | unfold, 42 | } 43 | -------------------------------------------------------------------------------- /src/Stream/utils/intercalate.ts: -------------------------------------------------------------------------------- 1 | import { Monoid } from 'fp-ts/lib/Monoid' 2 | 3 | import { Stream } from '../uri' 4 | import { intersperse } from './intersperse' 5 | 6 | /** 7 | * Places an element between members of a {@link Stream}. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @param {Monoid} M The monoid instance. 12 | * @return {(middle: A) => (fa: Stream) => A} A function that takes the 13 | * element to place between members of the stream. 14 | * 15 | * @__PURE__ 16 | */ 17 | export function intercalate(M: Monoid) { 18 | /** 19 | * Takes a value to place between members of a {@link Stream}. 20 | * 21 | * @param {A} middle The value to place. 22 | * @return {(fa: Stream) => A} A function that takes a stream and returns 23 | * the value. 24 | * 25 | * @step 1 26 | * @__PURE__ 27 | */ 28 | return function _intercalate(middle: A) { 29 | /** 30 | * Takes a stream and places the previously given element between the 31 | * members of the given stream. 32 | * 33 | * @param {Stream} fa The input stream. 34 | * @return {A} The concatenated value with the previously given monoid 35 | * instance. 36 | * 37 | * @step 2 38 | * @__PURE__ 39 | */ 40 | return function __intercalate(fa: Stream): A { 41 | let curr = M.empty 42 | const interspersed = intersperse(middle)(fa) 43 | 44 | for (const a of interspersed()) { 45 | curr = M.concat(curr, a) 46 | } 47 | 48 | return curr 49 | } 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /src/Stream/utils/lookup.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from 'fp-ts/lib/function' 2 | import { none, Option } from 'fp-ts/lib/Option' 3 | 4 | import { Stream } from '../uri' 5 | import { dropLeft } from './drop-left' 6 | import { head } from './head' 7 | import { take } from './take-left' 8 | 9 | /** 10 | * Provides a safe way to read a value at a particular index from 11 | * a {@link Stream}. 12 | * 13 | * If the index is negative, `None` will be returned. 14 | * 15 | * @export 16 | * @param {number} i The index to lookup. 17 | * @return {(fa: Stream) => Option} A function that takes a 18 | * stream to lookup at given index. 19 | * 20 | * @__PURE__ 21 | */ 22 | export function lookup(i: number): (fa: Stream) => Option 23 | 24 | /** 25 | * Provides a safe way to read a value at a particular index from 26 | * a {@link Stream}. 27 | * 28 | * If the index is negative, `None` will be returned. 29 | * 30 | * @export 31 | * @param {number} i The index to lookup. 32 | * @param {Stream} fa The input stream 33 | * @return {Option} An option of the element at index. 34 | * 35 | * @__PURE__ 36 | */ 37 | export function lookup(i: number, fa: Stream): Option 38 | export function lookup(i: number, fa?: Stream) { 39 | if (typeof fa === 'undefined') { 40 | return function _lookup(ma: Stream): Option { 41 | return lookup(i, ma) 42 | } 43 | } 44 | else if (i < 0) { return none } 45 | else { 46 | return pipe( 47 | fa, 48 | dropLeft(i), 49 | take(1), 50 | head 51 | ) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Stream/utils/rotate.ts: -------------------------------------------------------------------------------- 1 | import { rotate as arrayRotate } from 'fp-ts/lib/Array' 2 | import { pipe } from 'fp-ts/lib/function' 3 | 4 | import { concat } from '../concat' 5 | import { fromIterable, toArray } from '../conversions' 6 | import { Stream } from '../uri' 7 | import { dropLeft } from './drop-left' 8 | import { take } from './take-left' 9 | 10 | /** 11 | * Rotates a {@link Stream} by `n` steps. 12 | * 13 | * If the `n` is less than zero, the {@link Stream} will be rotated to the 14 | * opposite direction and **this case will consume the stream**. 15 | * 16 | * For `n` greater or equal to zero, 17 | * _this function will not consume the stream._ 18 | * 19 | * @export 20 | * @param {number} n The number of times to rotate. 21 | * @return {(fa: Stream) => Stream} A function that takes a stream to 22 | * rotate. 23 | * 24 | * @__PURE__ 25 | */ 26 | export function rotate(n: number) { 27 | /** 28 | * Takes a {@link Stream} to rotate by previously given steps. 29 | * 30 | * @template A The value type. 31 | * @param {Stream} fa The input stream. 32 | * @return {Stream} The output stream. 33 | * 34 | * @step 1 35 | * @__PURE__ 36 | */ 37 | return function _rotate(fa: Stream): Stream { 38 | if (n == 0) return fa 39 | if (n < 0) { 40 | return pipe( 41 | fa, 42 | toArray, 43 | arrayRotate(n), 44 | fromIterable 45 | ) 46 | } 47 | 48 | const left = pipe(fa, dropLeft(n)) 49 | const right = pipe(fa, take(n)) 50 | return concat(right)(left) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Stream/utils/take-left.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from '../uri' 2 | import { empty } from '../zero' 3 | 4 | /** 5 | * Takes given amount of items from a {@link Stream}. 6 | * 7 | * If negative value is passed, an empty {@link Stream} will be returned. 8 | * 9 | * @export 10 | * @param {number} count The number of elements to take. 11 | * @return {(fa: Stream) => Stream} A function that takes a stream and 12 | * returns another stream that contains only given amount of elements. 13 | * 14 | * @__PURE__ 15 | */ 16 | export function takeLeft(count: number) { 17 | /** 18 | * Takes previously given amount of elements from the given {@link Stream} 19 | * and returns another one contains that much amount of items. 20 | * 21 | * @template A The value type. 22 | * @param {Stream} fa The input stream. 23 | * @return {Stream} The output stream. 24 | * 25 | * @step 1 26 | * @__PURE__ 27 | */ 28 | return function _take(fa: Stream): Stream { 29 | if (count <= 0) return empty 30 | 31 | return function* __take() { 32 | for (const a of fa()) { 33 | yield a 34 | --count 35 | 36 | if (count == 0) break 37 | } 38 | } 39 | } 40 | } 41 | 42 | /** 43 | * Takes given amount of items from a {@link Stream}. 44 | * 45 | * @export 46 | * @param {number} count The number of elements to take. 47 | * @return {(fa: Stream) => Stream} A function that takes a stream and 48 | * returns another stream that contains only given amount of elements. 49 | * 50 | * @__PURE__ 51 | */ 52 | export const take = takeLeft 53 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/drop-left.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from '../uri' 2 | 3 | /** 4 | * Drops/skips the given amount of items from an {@link AsyncStream}. 5 | * 6 | * @export 7 | * @param {number} count The number of elements to drop. 8 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 9 | * async stream and returns another async stream that skips the given amount 10 | * of elements. 11 | * 12 | * @__PURE__ 13 | */ 14 | export function dropLeft(count: number) { 15 | /** 16 | * Skips the previously given amount of elements from the given 17 | * {@link AsyncStream} and returns another one skips that much of amount 18 | * items. 19 | * 20 | * @template A The value type. 21 | * @param {AsyncStream} fa The input stream. 22 | * @return {AsyncStream} the output stream. 23 | * 24 | * @step 1 25 | * @__PURE__ 26 | */ 27 | return function _dropLeft(fa: AsyncStream): AsyncStream { 28 | if (count === 0) return fa 29 | 30 | return async function* __dropLeft() { 31 | const gen = fa() 32 | for (; !(await gen.next()).done && count > 1; --count); 33 | yield* gen 34 | } 35 | } 36 | } 37 | 38 | /** 39 | * Drops/skips the given amount of items from an {@link AsyncStream}. 40 | * 41 | * @export 42 | * @param {number} count The number of elements to drop. 43 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 44 | * async stream and returns another async stream that skips the given amount 45 | * of elements. 46 | * 47 | * @__PURE__ 48 | */ 49 | export const skip = dropLeft 50 | -------------------------------------------------------------------------------- /src/Stream/applicative.ts: -------------------------------------------------------------------------------- 1 | import { Applicative1 } from 'fp-ts/lib/Applicative' 2 | 3 | import { Functor } from './functor' 4 | import { Pointed } from './pointed' 5 | import { Stream, URI } from './uri' 6 | 7 | /** 8 | * Applies a {@link Stream} of type `A` to {@link Stream} of functions 9 | * from `A` to `B`. 10 | * 11 | * @export 12 | * @template A The value type. 13 | * @param {Stream} fa The source stream. 14 | * @return {(fab: Stream<(a: A) => B>) => Stream} A function that takes 15 | * a {@link Stream} of functions. 16 | * 17 | * @__PURE__ 18 | */ 19 | export function ap(fa: Stream) { 20 | /** 21 | * Applies previously given {@link Stream} to the given {@link Stream} of 22 | * functions that take the items of the previously given {@link Stream} and 23 | * returns `B` values. 24 | * 25 | * @template B The output value type. 26 | * @param {Stream<(a: A) => B>} fab The function stream to apply the values 27 | * of the previously given stream to its functions returning `B` value. 28 | * 29 | * @return {Stream} The output stream. 30 | * @step 1 31 | * 32 | * @__PURE__ 33 | */ 34 | return function _ap(fab: Stream<(a: A) => B>): Stream { 35 | return function* __ap() { 36 | for (const a of fa()) { 37 | for (const f of fab()) { 38 | yield f(a) 39 | } 40 | } 41 | } 42 | } 43 | } 44 | 45 | /** 46 | * The `Applicative` category instance for {@link Stream}. 47 | */ 48 | export const Applicative: Applicative1 = { 49 | URI, 50 | map: Functor.map, 51 | of: Pointed.of, 52 | ap(fab, fa) { return ap(fa)(fab) } 53 | } 54 | -------------------------------------------------------------------------------- /src/Stream/utils/chop.ts: -------------------------------------------------------------------------------- 1 | import { Stream } from '../uri' 2 | import { prepend } from './prepend' 3 | 4 | /** 5 | * A useful recursion pattern for processing a {@link Stream} to produce a new 6 | * array, often used for "chopping" up the input {@link Stream}. Typically 7 | * `chop` is called with some function that will consume an initial prefix of 8 | * the {@link Stream} and produce a value and the rest of the {@link Stream}. 9 | * 10 | * @export 11 | * @template A The value type. 12 | * @template B The chopped value type. 13 | * @param {(fa: Stream) => [ B, Stream ]} f The chop function. 14 | * @return {(fa: Stream) => Stream} A function that takes a stream to 15 | * chop the first element of it. 16 | * 17 | * @__PURE__ 18 | */ 19 | export function chop(f: (fa: Stream) => [ B, Stream ]) { 20 | /** 21 | * Chops the given {@link Stream} and returns another {@link Stream} based 22 | * on the previously given function. 23 | * 24 | * @param {Stream} fa The input stream. 25 | * @return {Stream} The output stream. 26 | * 27 | * @__PURE__ 28 | */ 29 | return function _chop(fa: Stream): Stream { 30 | return function* __chop() { 31 | let target = fa 32 | 33 | while (true) { 34 | const [ b, nextStream ] = f(target) 35 | yield b 36 | 37 | // Executing the stream manually to prevent invoking the next twice. 38 | const nextGen = nextStream() 39 | const { value, done } = nextGen.next() 40 | 41 | if (done) { return } 42 | 43 | target = prepend(value)(() => nextGen) 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/none.ts: -------------------------------------------------------------------------------- 1 | import { Refinement } from 'fp-ts/lib/Refinement' 2 | import { Task } from 'fp-ts/lib/Task' 3 | 4 | import { AsyncStream } from '../uri' 5 | import { AsyncPredicate } from './async-predicate' 6 | import { every } from './every' 7 | 8 | /** 9 | * `none` tells if the provided refinement holds `false` for every element 10 | * in the {@link AsyncStream}. 11 | * 12 | * @export 13 | * @template A The value type. 14 | * @template B The refined value type. 15 | * @param {Refinement} refinement The refinement function. 16 | * @return {Task} `true` if all the 17 | * elements returned `false` from the refinement function. 18 | * 19 | * @__PURE__ 20 | */ 21 | export function none( 22 | refinement: Refinement 23 | ): (fa: AsyncStream) => Task 24 | 25 | /** 26 | * `none` tells if the provided predicate holds false for none element 27 | * in the {@link AsyncStream}. 28 | * 29 | * @export 30 | * @template A The value type. 31 | * @template B The refined value type. 32 | * @param {AsyncPredicate} predicate The predicate function. 33 | * @return {AsyncPredicate>} `true` if all the elements 34 | * returned `false` from the refinement function. 35 | * 36 | * @__PURE__ 37 | */ 38 | export function none(predicate: AsyncPredicate): (fa: AsyncStream) => Task 39 | export function none(predicate: AsyncPredicate) { 40 | return every(it => { 41 | const result = predicate(it) 42 | if (typeof result === 'object') { 43 | return result.then(value => !value) 44 | } 45 | else { 46 | return !result 47 | } 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/lookup.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from 'fp-ts/lib/function' 2 | import { none, Option } from 'fp-ts/lib/Option' 3 | import { Task } from 'fp-ts/lib/Task' 4 | 5 | import { AsyncStream } from '../uri' 6 | import { dropLeft } from './drop-left' 7 | import { head } from './head' 8 | import { take } from './take-left' 9 | 10 | /** 11 | * Provides a safe way to read a value at a particular index from 12 | * an {@link AsyncStream}. 13 | * 14 | * If the index is negative, `None` will be returned. 15 | * 16 | * @export 17 | * @param {number} i The index to lookup. 18 | * @return {(fa: AsyncStream) => Task>} A function that takes a 19 | * stream to lookup at given index. 20 | * 21 | * @__PURE__ 22 | */ 23 | export function lookup(i: number): (fa: AsyncStream) => Task> 24 | 25 | /** 26 | * Provides a safe way to read a value at a particular index from 27 | * an {@link AsyncStream}. 28 | * 29 | * If the index is negative, `None` will be returned. 30 | * 31 | * @export 32 | * @param {number} i The index to lookup. 33 | * @param {AsyncStream} fa The input stream 34 | * @return {Task>} An option of the element at index. 35 | * 36 | * @__PURE__ 37 | */ 38 | export function lookup(i: number, fa: AsyncStream): Task> 39 | export function lookup(i: number, fa?: AsyncStream) { 40 | if (typeof fa === 'undefined') { 41 | return function _lookup(ma: AsyncStream): Task> { 42 | return lookup(i, ma) 43 | } 44 | } 45 | else if (i < 0) { return none } 46 | else { 47 | return pipe( 48 | fa, 49 | dropLeft(i), 50 | take(1), 51 | head 52 | ) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/every.ts: -------------------------------------------------------------------------------- 1 | import { Refinement } from 'fp-ts/lib/Refinement' 2 | import { Task } from 'fp-ts/lib/Task' 3 | 4 | import { AsyncStream } from '../uri' 5 | import { AsyncPredicate } from './async-predicate' 6 | 7 | /** 8 | * `every` tells if the provided refinement holds true for every element 9 | * in the {@link AsyncStream}. 10 | * 11 | * @export 12 | * @template A The value type. 13 | * @template B The refined value type. 14 | * @param {Refinement} refinement The refinement function. 15 | * @return {(fa: AsyncStream) => Task} `true` if all the 16 | * elements returned `true` from the refinement function. 17 | * 18 | * @__PURE__ 19 | */ 20 | export function every( 21 | refinement: Refinement 22 | ): (fa: AsyncStream) => Task 23 | 24 | /** 25 | * `every` tells if the provided predicate holds true for every element 26 | * in the {@link AsyncStream}. 27 | * 28 | * @export 29 | * @template A The value type. 30 | * @template B The refined value type. 31 | * @param {PredAsyncPredicateicate} predicate The predicate function. 32 | * @return {AsyncPredicate>} `true` if all the elements 33 | * returned `true` from the refinement function. 34 | * 35 | * @__PURE__ 36 | */ 37 | export function every(predicate: AsyncPredicate): (fa: AsyncStream) => Task 38 | export function every(predicate: AsyncPredicate): (fa: AsyncStream) => Task { 39 | return function _every(ma) { 40 | return async function _every() { 41 | for await (const a of ma()) { 42 | if (!(await predicate(a))) return false 43 | } 44 | 45 | return true 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/drop-right.ts: -------------------------------------------------------------------------------- 1 | import { identity } from 'fp-ts/lib/function' 2 | 3 | import { fromIterable, toArray } from '../conversions' 4 | import { AsyncStream } from '../uri' 5 | import { take } from './take-left' 6 | 7 | /** 8 | * Drops/skips the given amount of items from an {@link AsyncStream} from end to 9 | * the start. 10 | * 11 | * **Warning: This function consumes the stream.** 12 | * 13 | * - Negative values will be equal to {@link take} as much as the value. 14 | * - If `0` is passed, the stream itself will be returned. 15 | * 16 | * @export 17 | * @param {number} count The number of elements to drop. 18 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 19 | * async stream and returns another async stream that skips the given amount of 20 | * elements. 21 | * 22 | * @__PURE__ 23 | */ 24 | export function dropRight(count: number) { 25 | if (count < 0) return take(-count) 26 | else if (count == 0) return identity 27 | 28 | /** 29 | * Skips the previously given amount of elements from the given 30 | * {@link AsyncStream} and returns another one skips that much of amount 31 | * items. 32 | * 33 | * **Warning: This function consumes the stream.** 34 | * 35 | * @template A The value type. 36 | * @param {AsyncStream} fa The input stream. 37 | * @return {AsyncStream} the output stream. 38 | * 39 | * @step 1 40 | * @__PURE__ 41 | */ 42 | return function _dropRight(fa: AsyncStream): AsyncStream { 43 | return async function* __dropRight() { 44 | const items = await toArray(fa) 45 | const source = items.slice(0, -count) 46 | return fromIterable(source) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Stream/extend.ts: -------------------------------------------------------------------------------- 1 | import { Extend1 } from 'fp-ts/lib/Extend' 2 | import { pipe } from 'fp-ts/lib/function' 3 | 4 | import { Functor, mapWithIndex } from './functor' 5 | import { Stream, URI } from './uri' 6 | import { dropLeft } from './utils/drop-left' 7 | 8 | /** 9 | * Given an iterating function that takes a {@link Stream} as input, `extend` 10 | * returns a {@link Stream} containing the results of the iterating function 11 | * applied to the whole input {@link Stream}, then to the input {@link Stream} 12 | * without the first element, then to the input {@link Stream} without the first 13 | * two elements, etc. 14 | * 15 | * @export 16 | * @template A The input value type. 17 | * @template B The output value. 18 | * @param {(ma: Stream) => B} f The mapping function. 19 | * @return {(fa: Stream) => Stream} A function that takes a stream of 20 | * type `A` and returns another stream of type `B`. 21 | * 22 | * @__PURE__ 23 | */ 24 | export function extend(f: (fa: Stream) => B) { 25 | /** 26 | * Takes an input {@link Stream} to map with the previously given function 27 | * and returns another {@link Stream} of results. 28 | * 29 | * @param {Stream} fa The input stream. 30 | * @return {Stream} The output stream. 31 | * 32 | * @step 1 33 | * @__PURE__ 34 | */ 35 | return function _extend(fa: Stream): Stream { 36 | return pipe( 37 | fa, 38 | mapWithIndex((i, _) => f(pipe(fa, dropLeft(i)))) 39 | ) 40 | } 41 | } 42 | 43 | /** 44 | * The `Extend` category instance for {@link Stream}. 45 | */ 46 | export const Extend: Extend1 = { 47 | URI, 48 | map: Functor.map, 49 | extend(wa, f) { return extend(f)(wa) }, 50 | } 51 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/intercalate.ts: -------------------------------------------------------------------------------- 1 | import { Monoid } from 'fp-ts/lib/Monoid' 2 | import { Task } from 'fp-ts/lib/Task' 3 | 4 | import { AsyncStream } from '../uri' 5 | import { intersperse } from './intersperse' 6 | 7 | /** 8 | * Places an element between members of an {@link AsyncStream}. 9 | * 10 | * @export 11 | * @template A The value type. 12 | * @param {Monoid} M The monoid instance. 13 | * @return {(middle: A) => (fa: AsyncStream) => Task} A function that 14 | * takes the element to place between members of the stream. 15 | * 16 | * @__PURE__ 17 | */ 18 | export function intercalate(M: Monoid) { 19 | /** 20 | * Takes a value to place between members of an {@link AsyncStream}. 21 | * 22 | * @param {A} middle The value to place. 23 | * @return {(fa: AsyncStream) => A} A function that takes an async stream 24 | * and returns the value. 25 | * 26 | * @step 1 27 | * @__PURE__ 28 | */ 29 | return function _intercalate(middle: A) { 30 | /** 31 | * Takes a stream and places the previously given element between the 32 | * members of the given stream. 33 | * 34 | * @param {Stream} fa The input stream. 35 | * @return {A} The concatenated value with the previously given monoid 36 | * instance. 37 | * 38 | * @step 2 39 | * @__PURE__ 40 | */ 41 | return function __intercalate(fa: AsyncStream): Task { 42 | return async function ___intercalate() { 43 | let curr = M.empty 44 | const interspersed = intersperse(middle)(fa) 45 | 46 | for await (const a of interspersed()) { 47 | curr = M.concat(curr, a) 48 | } 49 | 50 | return curr 51 | } 52 | } 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/take-left.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from '../uri' 2 | import { empty } from '../zero' 3 | 4 | /** 5 | * Takes given amount of items from an {@link AsyncStream}. 6 | * 7 | * If negative value is passed, an empty {@link AsyncStream} will be returned. 8 | * 9 | * @export 10 | * @param {number} count The number of elements to take. 11 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 12 | * async stream and returns another async stream that contains only given 13 | * amount of elements. 14 | * 15 | * @__PURE__ 16 | */ 17 | export function takeLeft(count: number) { 18 | /** 19 | * Takes previously given amount of elements from the given 20 | * {@link AsyncStream} and returns another one contains that much amount 21 | * of items. 22 | * 23 | * @template A The value type. 24 | * @param {AsyncStream} fa The input async stream. 25 | * @return {AsyncStream} The output async stream. 26 | * 27 | * @step 1 28 | * @__PURE__ 29 | */ 30 | return function _take(fa: AsyncStream): AsyncStream { 31 | if (count <= 0) return empty 32 | 33 | return async function* __take() { 34 | for await (const a of fa()) { 35 | yield a 36 | --count 37 | 38 | if (count == 0) break 39 | } 40 | } 41 | } 42 | } 43 | 44 | /** 45 | * Takes given amount of items from an {@link AsyncStream}. 46 | * 47 | * @export 48 | * @param {number} count The number of elements to take. 49 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 50 | * async stream and returns another async stream that contains only given 51 | * amount of elements. 52 | * 53 | * @__PURE__ 54 | */ 55 | export const take = takeLeft 56 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | This file contains the changes made to the package 3 | 4 | This sections are in descending order of the change date. 5 | 6 | ## [0.1.11] - 2023-06-04 7 | ### Added 8 | - `minimum` and `maximum` functions for both `Stream` and `AsyncStream`. 9 | - `allM` and `anyM` functions that checks if all/any of the values that `Stream` 10 | generates is `true`. 11 | - `cartesian` function for both `Stream` and `AsyncStream`. 12 | - `countBy` function for both `Stream` and `AsyncStream`. 13 | - `filterA` function for `Stream`. 14 | - `transpose` function for both `Stream` and `AsyncStream`. 15 | 16 | ### Changed 17 | - `fp-ts` and `tslib` dependencies are now `peerDependencies`. 18 | 19 | ## [0.1.10] - 2022-10-03 20 | ### Changes 21 | Updated `README` file example. 22 | 23 | ## [0.1.9] - 2022-09-30 24 | ### Changes 25 | 26 | Updated types to provide `async` functions 27 | 28 | Now the following functions may also take functions that 29 | return `Promise` for their results: 30 | - `comprehension` 31 | - `dropRightWhile` 32 | - `findFirstMap` 33 | - `findLastMap` 34 | - `flap` 35 | - `tail` 36 | - `apSeq` 37 | - `ap` 38 | - `filterMapWithIndex` 39 | - `filterMap` 40 | - `partitionMap` 41 | - `partitionMapWithIndex` 42 | - `unfold` 43 | 44 | 45 | Likewise the following instance implementations are updated: 46 | 47 | - `ApplicativeSeq` 48 | - `Applicative` 49 | - `Filterable` 50 | - `Unfoldable` 51 | 52 | 53 | ## [0.1.0] - 2022-09-26 54 | Added initial commit 55 | 56 | [0.1.11]: https://github.com/incetarik/fp-ts-stream/compare/0.1.10...0.1.11 57 | [0.1.10]: https://github.com/incetarik/fp-ts-stream/compare/0.1.9...0.1.10 58 | [0.1.9]: https://github.com/incetarik/fp-ts-stream/compare/0.1.8...0.1.9 59 | [0.1.0]: https://github.com/incetarik/fp-ts-stream 60 | -------------------------------------------------------------------------------- /src/Stream/utils/transpose.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from 'fp-ts/lib/function' 2 | 3 | import { fromIterable, toArray } from '../conversions' 4 | import { map } from '../functor' 5 | 6 | import type { Stream } from "../uri"; 7 | 8 | /** 9 | * Transposes the rows and columns of a 2D {@link Stream}. 10 | * 11 | * If some of the rows are shorter than the following rows, their elements are skipped. 12 | * 13 | * @export 14 | * @template A The type of the values. 15 | * @param {Stream>} xs The stream of a stream of the values. 16 | * @return {Stream>} A new stream of transposed values. 17 | * 18 | * @__PURE__ 19 | */ 20 | export function transpose(xs: Stream>): Stream> { 21 | return pipe( 22 | xs, 23 | map(toArray), 24 | transposeArray, 25 | map(fromIterable) 26 | ) 27 | } 28 | 29 | /** 30 | * Transposes the rows and columns of a 2D {@link Stream}. 31 | * 32 | * If some of the rows are shorter than the following rows, their elements are skipped. 33 | * 34 | * @export 35 | * @template A The type of the values. 36 | * @param {Stream>} xs The stream of the array of the values. 37 | * @return {Stream>} A new stream of transposed values. 38 | * 39 | * @__PURE__ 40 | */ 41 | export function transposeArray(xs: Stream>): Stream> { 42 | return function* _transpose() { 43 | const sources = toArray(xs) 44 | 45 | let i = 0 46 | while (true) { 47 | const current = [] as A[] 48 | 49 | for (const source of sources) { 50 | if (source.length <= i) continue 51 | current.push(source[ i ]) 52 | } 53 | 54 | i++ 55 | if (current.length) { 56 | yield current 57 | } 58 | else { 59 | break 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/AsyncStream/extend.ts: -------------------------------------------------------------------------------- 1 | import { Extend1 } from 'fp-ts/lib/Extend' 2 | import { pipe } from 'fp-ts/lib/function' 3 | 4 | import { Functor, mapWithIndex } from './functor' 5 | import { AsyncStream, URI } from './uri' 6 | import { dropLeft } from './utils/drop-left' 7 | 8 | /** 9 | * Given an iterating function that takes an {@link AsyncStream} as input, 10 | * `extend` returns an {@link AsyncStream} containing the results of the 11 | * iterating function applied to the whole input {@link AsyncStream}, then to 12 | * the input {@link AsyncStream} without the first element, then to the 13 | * input {@link AsyncStream} without the first two elements, etc. 14 | * 15 | * @export 16 | * @template A The input value type. 17 | * @template B The output value. 18 | * @param {(ma: AsyncStream) => B} f The mapping function. 19 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 20 | * async stream of type `A` and returns another async stream of type `B`. 21 | * 22 | * @__PURE__ 23 | */ 24 | export function extend(f: (fa: AsyncStream) => B) { 25 | /** 26 | * Takes an input {@link AsyncStream} to map with the previously given 27 | * function and returns another {@link AsyncStream} of results. 28 | * 29 | * @param {AsyncStream} fa The input async stream. 30 | * @return {AsyncStream} The output async stream. 31 | * 32 | * @step 1 33 | * @__PURE__ 34 | */ 35 | return function _extend(fa: AsyncStream): AsyncStream { 36 | return pipe( 37 | fa, 38 | mapWithIndex((i, _) => f(pipe(fa, dropLeft(i)))) 39 | ) 40 | } 41 | } 42 | 43 | /** 44 | * The `Extend` category instance for {@link AsyncStream}. 45 | */ 46 | export const Extend: Extend1 = { 47 | URI, 48 | map: Functor.map, 49 | extend(wa, f) { return extend(f)(wa) }, 50 | } 51 | -------------------------------------------------------------------------------- /src/Stream/transformations/monoid.ts: -------------------------------------------------------------------------------- 1 | import { Eq } from 'fp-ts/lib/Eq' 2 | import { Monoid } from 'fp-ts/lib/Monoid' 3 | 4 | import { concat } from '../concat' 5 | import { Stream } from '../uri' 6 | import { empty } from '../zero' 7 | import { getUnionSemigroup } from './semigroup' 8 | 9 | /** 10 | * Returns a {@link Monoid} for {@link Stream}. 11 | * 12 | * @export 13 | * @template A The value type. 14 | * @return {Monoid>} A {@link Monoid} instance for {@link Stream} 15 | * of type `A`. 16 | * 17 | * @example 18 | * import { getMonoid, fromIterable, toArray } from 'fp-ts-stream/Stream' 19 | * const M = getMonoid() 20 | * 21 | * assert.deepStrictEqual( 22 | * toArray(M.concat(fromIterable([1, 2]), fromIterable([3, 4]))), 23 | * [1, 2, 3, 4] 24 | * ) 25 | * 26 | * @category instances 27 | * @__PURE__ 28 | */ 29 | export function getMonoid(): Monoid> { 30 | return { 31 | empty, 32 | concat: (x, y) => concat(y)(x) 33 | } 34 | } 35 | 36 | /** 37 | * Returns a {@link Monoid} for {@link Stream} which contains the union 38 | * of the elements. 39 | * 40 | * @export 41 | * @template A The value type. 42 | * @param {Eq} E The {@link Eq} instance for type `A`. 43 | * @return {Monoid>} A {@link Monoid} instance for {@link Stream} 44 | * of type `A`. 45 | * 46 | * @example 47 | * import { getMonoid, fromIterable, toArray } from 'fp-ts-stream/Stream' 48 | * const M = getMonoid() 49 | * 50 | * assert.deepStrictEqual( 51 | * toArray(M.concat(fromIterable([1, 2]), fromIterable([2, 3, 4]))), 52 | * [1, 2, 3, 4] 53 | * ) 54 | * 55 | * @category instances 56 | * @__PURE__ 57 | */ 58 | export function getUnionMonoid(E: Eq): Monoid> { 59 | return { 60 | empty, 61 | concat: getUnionSemigroup(E).concat, 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/AsyncStream/transformations/from-async-predicate.ts: -------------------------------------------------------------------------------- 1 | import { of } from '../pointed' 2 | import { AsyncStream } from '../uri' 3 | import { AsyncPredicate } from '../utils/async-predicate' 4 | import { empty } from '../zero' 5 | 6 | /** 7 | * Creates a {@link AsyncStream} from a {@link AsyncPredicate}. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @template B The refined value type. 12 | * @param {AsyncPredicate} predicate The refinement function. 13 | * @return {(b: B) => AsyncStream} A function that will take a 14 | * value and return an async stream of it. 15 | * 16 | * @__PURE__ 17 | */ 18 | export function fromAsyncPredicate(predicate: AsyncPredicate): (b: B) => AsyncStream 19 | 20 | /** 21 | * Creates an {@link AsyncStream} from an {@link AsyncPredicate}. 22 | * 23 | * @export 24 | * @template A The value type. 25 | * @template B The refined value type. 26 | * @param {AsyncPredicate} predicate The refinement function. 27 | * @return {(a: A) => AsyncStream} A function that will take a value 28 | * and return an async stream of it. 29 | * 30 | * @__PURE__ 31 | */ 32 | export function fromAsyncPredicate(predicate: AsyncPredicate): (a: A) => AsyncStream 33 | export function fromAsyncPredicate(predicate: AsyncPredicate) { 34 | /** 35 | * Takes a value to apply the previously given predicate to create an 36 | * {@link AsyncStream} instance. 37 | * 38 | * @param {A} a The input value. 39 | * @return {AsyncStream} The output async stream. 40 | * 41 | * @step 1 42 | * @__PURE__ 43 | */ 44 | return function _fromAsyncPredicate(a: A): AsyncStream { 45 | return async function* __fromAsyncPredicate() { 46 | if (await predicate(a)) return of(a) 47 | else { 48 | return empty 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/AsyncStream/zero.ts: -------------------------------------------------------------------------------- 1 | import { guard as zeroGuard, Zero1 } from 'fp-ts/lib/Zero' 2 | 3 | import { Pointed } from './pointed' 4 | import { AsyncStream, URI } from './uri' 5 | 6 | /** 7 | * Returns an empty {@link AsyncStream} of type `A`. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @return {AsyncStream} A {@link AsyncStream} instance that will yield no 12 | * values. 13 | * 14 | * @category model 15 | * @__PURE__ 16 | */ 17 | export function zero(): AsyncStream { 18 | return async function* _zero() {} 19 | } 20 | 21 | /** 22 | * An empty {@link AsyncStream} instance. 23 | */ 24 | export const empty = zero() 25 | 26 | /** 27 | * The `Zero` category instance for {@link AsyncStream}. 28 | * 29 | * @category model 30 | */ 31 | export const Zero: Zero1 = { 32 | URI, 33 | zero, 34 | } 35 | 36 | /** 37 | * @category do notation 38 | */ 39 | export const guard = zeroGuard(Zero, Pointed) 40 | 41 | /** 42 | * An {@link AsyncStream} that never completes. 43 | * 44 | * @export 45 | * @template A The value type. 46 | * @return {AsyncStream} The async stream that never completes. 47 | * 48 | * @__PURE__ 49 | */ 50 | export function makeNever(): AsyncStream { 51 | return function _never(): AsyncGenerator { 52 | return { 53 | next() { 54 | return new Promise>(_resolve => { 55 | // Never resolves 56 | }) 57 | }, 58 | return() { 59 | return Promise.resolve({ done: true, value: undefined }) 60 | }, 61 | throw: Promise.reject, 62 | [ Symbol.asyncIterator ]() { return this } 63 | } 64 | } 65 | } 66 | 67 | /** 68 | * An {@link AsyncStream} that never completes. 69 | * 70 | * @export 71 | * @template A The value type. 72 | * 73 | * @__PURE__ 74 | */ 75 | export const never = makeNever() 76 | -------------------------------------------------------------------------------- /src/Stream/traversable-array.ts: -------------------------------------------------------------------------------- 1 | import { identity, pipe } from 'fp-ts/lib/function' 2 | 3 | import { Stream } from './uri' 4 | 5 | /** 6 | * Equivalent to `ReadonlyArray.traverse(Applicative)` 7 | * 8 | * @export 9 | * @template A The value type. 10 | * @template B The output type. 11 | * @param {(a: A) => Stream} f The mapping function. 12 | * @return {(as: ReadonlyArray) => Stream>} A function that 13 | * takes an array of `A` and returns a stream of array of `B`. 14 | * 15 | * @category traversing 16 | * @__PURE__ 17 | */ 18 | export function traverseArray(f: (a: A) => Stream) { 19 | /** 20 | * Takes an array of `A` and returns a {@link Stream} of array of `B`. 21 | * 22 | * @param {ReadonlyArray} as The input array. 23 | * @return {Stream>} The output stream. 24 | * 25 | * @category traversing 26 | * @step 1 27 | * @__PURE__ 28 | */ 29 | return function _traverseArray(as: ReadonlyArray): Stream> { 30 | return function* __traverseArray() { 31 | const result: B[] = [] 32 | 33 | for (const a of as) { 34 | const gen = f(a)() 35 | let { value, done } = gen.next() 36 | 37 | if (done) { 38 | yield [] 39 | return 40 | } 41 | 42 | result.push(value) 43 | for (const b of gen) { result.push(b) } 44 | } 45 | 46 | yield result 47 | } 48 | } 49 | } 50 | 51 | /** 52 | * Equivalent to `ReadonlyArray.sequence(Applicative)` 53 | * 54 | * @export 55 | * @template A The value type. 56 | * @param {ReadonlyArray>} arr The input array. 57 | * @return {Stream>} The output stream. 58 | * 59 | * @category traversing 60 | * @__PURE__ 61 | */ 62 | export function sequenceArray(arr: ReadonlyArray>): Stream> { 63 | return pipe( 64 | arr, 65 | traverseArray(identity) 66 | ) 67 | } 68 | -------------------------------------------------------------------------------- /src/Stream/utils/match.ts: -------------------------------------------------------------------------------- 1 | import { Lazy } from 'fp-ts/lib/function' 2 | 3 | import { Stream } from '../uri' 4 | import { isEmpty } from './is-empty' 5 | 6 | /** 7 | * Less strict version of {@link match}. 8 | * 9 | * @export 10 | * @template B The type of the value on empty. 11 | * @template A The type of the streamed value. 12 | * @template C The type of the value on non-empty. 13 | * @param {Lazy} onEmpty The lazy function that will be executed if the 14 | * stream is empty. 15 | * 16 | * @param {(fa: Stream) => C} onNonEmpty The function that will be executed 17 | * if the stream is not empty. 18 | * 19 | * @return {(fa: Stream) => B | C} A function that takes a stream to match. 20 | * 21 | * @category pattern matching 22 | * @__PURE__ 23 | */ 24 | export function matchW(onEmpty: Lazy, onNonEmpty: (fa: Stream) => C) { 25 | /** 26 | * Takes a {@link Stream} to match. 27 | * 28 | * @param {Stream} fa The stream. 29 | * @return {B | C} The match value. 30 | * 31 | * @category pattern matching 32 | * @__PURE__ 33 | */ 34 | return function _matchW(fa: Stream): B | C { 35 | if (isEmpty(fa)) { 36 | return onEmpty() 37 | } 38 | else { 39 | return onNonEmpty(fa) 40 | } 41 | } 42 | } 43 | 44 | /** 45 | * Matches a {@link Stream} whether if that was empty or not. 46 | * 47 | * @export 48 | * @template B The type of the value on empty. 49 | * @template A The type of the streamed value. 50 | * @param {Lazy} onEmpty The lazy function that will be executed if the 51 | * stream is empty. 52 | * 53 | * @param {(fa: Stream) => B} onNonEmpty The function that will be executed. 54 | * @return {(fa: Stream) => B} A function that takes a stream to match. 55 | * 56 | * @category pattern matching 57 | * @__PURE__ 58 | */ 59 | export function match(onEmpty: Lazy, onNonEmpty: (fa: Stream) => B) { 60 | return matchW(onEmpty, onNonEmpty) 61 | } 62 | -------------------------------------------------------------------------------- /src/Stream/utils/elem.ts: -------------------------------------------------------------------------------- 1 | import { Eq } from 'fp-ts/lib/Eq' 2 | import { pipe } from 'fp-ts/lib/function' 3 | import { isSome } from 'fp-ts/lib/Option' 4 | 5 | import { Stream } from '../uri' 6 | import { findFirst } from './find-first' 7 | 8 | /** 9 | * Tests if a value is a member of a {@link Stream}. 10 | * 11 | * Takes a {@link Eq} of `A` as a single argument which returns the function 12 | * to use to search for a value of type `A` in a {@link Stream}. 13 | * 14 | * **Warning: This function consumes the stream.** 15 | * 16 | * @export 17 | * @template A The value type. 18 | * @param {Eq} E The equality instance. 19 | * @return {(a: A) => (fa: Stream) => boolean} A function to use to search 20 | * for a value in the stream. 21 | * 22 | * @__PURE__ 23 | */ 24 | export function elem(E: Eq) { 25 | /** 26 | * Takes an element to test if that is a member of a {@link Stream}. 27 | * 28 | * @param {A} a The value to test if that is in the stream. 29 | * @return {(fa: Stream) => boolean} A function that will take 30 | * the stream to search. 31 | * 32 | * @step 1 33 | * @__PURE__ 34 | */ 35 | function _elem(a: A): (fa: Stream) => boolean 36 | 37 | /** 38 | * Takes an element to test if that is a member of a {@link Stream}. 39 | * 40 | * @param {A} a The value to test if that is in the stream. 41 | * @param {Stream} fa The stream to search the element. 42 | * @return {boolean} `true` if the element is a member of the stream, 43 | * `false` otherwise. 44 | * 45 | * @step 1 46 | * @__PURE__ 47 | */ 48 | function _elem(a: A, fa: Stream): boolean 49 | function _elem(a: A, fa?: Stream): ((fa: Stream) => boolean) | boolean { 50 | if (typeof fa !== 'undefined') { 51 | return pipe(fa, findFirst(_a => E.equals(a, _a)), isSome) 52 | } 53 | 54 | return function __elem(ma: Stream): boolean { 55 | return _elem(a, ma) 56 | } 57 | } 58 | 59 | return _elem 60 | } 61 | -------------------------------------------------------------------------------- /src/Stream/utils/union.ts: -------------------------------------------------------------------------------- 1 | import { Eq } from 'fp-ts/lib/Eq' 2 | 3 | import { Stream } from '../uri' 4 | 5 | /** 6 | * Creates a {@link Stream} of unique values, in order, from all given 7 | * {@link Stream}s using a {@link Eq} for equality comparisons. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @param {Eq} E The equality instance. 12 | * @return {(xs: Stream) => (ys: Stream) => Stream} A function that 13 | * takes streams. 14 | * 15 | * @__PURE__ 16 | */ 17 | export function union(E: Eq) { 18 | /** 19 | * Takes a {@link Stream} to union with another {@link Stream}. 20 | * 21 | * @param {Stream} xs The first stream. 22 | * @return {(ys: Stream) => Stream} A function that takes the 23 | * other stream. 24 | * 25 | * @step 1 26 | * @__PURE__ 27 | */ 28 | function _union(xs: Stream): (ys: Stream) => Stream 29 | 30 | /** 31 | * Takes a {@link Stream} to union with another {@link Stream}. 32 | * 33 | * @param {Stream} xs The first stream. 34 | * @param {Stream} ys The second stream. 35 | * @return {Stream} A new stream containing unique values from both 36 | * streams. 37 | * 38 | * @step 1 39 | * @__PURE__ 40 | */ 41 | function _union(xs: Stream, ys: Stream): Stream 42 | function _union(xs: Stream, ys?: Stream): Stream | ((ys: Stream) => Stream) { 43 | if (typeof ys !== 'undefined') { 44 | return function* __union() { 45 | const collected: A[] = [] 46 | for (const y of ys()) { 47 | yield y 48 | collected.push(y) 49 | } 50 | 51 | for (const x of xs()) { 52 | if (!collected.some(it => E.equals(it, x))) { 53 | yield x 54 | collected.push(x) 55 | } 56 | } 57 | } 58 | } 59 | 60 | return function _union_union(my: Stream): Stream { 61 | return _union(xs, my) 62 | } 63 | } 64 | 65 | return _union 66 | } 67 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/chop.ts: -------------------------------------------------------------------------------- 1 | import { AsyncStream } from '../uri' 2 | import { prepend } from './prepend' 3 | 4 | /** 5 | * A useful recursion pattern for processing an {@link AsyncStream} to produce 6 | * a new array, often used for "chopping" up the input {@link AsyncStream}. 7 | * 8 | * Typically `chop` is called with some function that will consume an initial 9 | * prefix of the {@link AsyncStream} and produce a value and the rest of the 10 | * {@link AsyncStream}. 11 | * 12 | * @export 13 | * @template A The value type. 14 | * @template B The chopped value type. 15 | * @param {(fa: AsyncStream) => [ B, AsyncStream ] | Promise<[ B, AsyncStream ]>} f The chop function. 16 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes a stream to 17 | * chop the first element of it. 18 | * 19 | * @__PURE__ 20 | */ 21 | export function chop(f: (fa: AsyncStream) => [ B, AsyncStream ] | Promise<[ B, AsyncStream ]>) { 22 | /** 23 | * Chops the given {@link AsyncStream} and returns another {@link AsyncStream} 24 | * based on the previously given function. 25 | * 26 | * @param {AsyncStream} fa The input async stream. 27 | * @return {AsyncStream} The output async stream. 28 | * 29 | * @__PURE__ 30 | */ 31 | return function _chop(fa: AsyncStream): AsyncStream { 32 | return async function* __chop() { 33 | let target = fa 34 | 35 | while (true) { 36 | const [ b, nextStream ] = await f(target) 37 | yield b 38 | 39 | // Executing the stream manually to prevent calling the generator 40 | // step twice as this `Promise` may be taking time to be calculated. 41 | const nextGen = nextStream() 42 | const { value, done } = await nextGen.next() 43 | 44 | if (done) { return } 45 | 46 | // If there are elements remaining to chop, prepend the fetched value 47 | // and continue with the rest. 48 | target = prepend(value)(() => nextGen) 49 | } 50 | } 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/Stream/compactable.ts: -------------------------------------------------------------------------------- 1 | import { Compactable1 } from 'fp-ts/lib/Compactable' 2 | import { Either, isLeft, isRight } from 'fp-ts/lib/Either' 3 | import { isSome, Option } from 'fp-ts/lib/Option' 4 | import { Separated } from 'fp-ts/lib/Separated' 5 | 6 | import { Stream, URI } from './uri' 7 | 8 | /** 9 | * Compact a {@link Stream} of {@link Option}s discarding the `None` values 10 | * and keeping the `Some` values. It returns a new {@link Stream} containing 11 | * the values of `Some` options. 12 | * 13 | * @export 14 | * @template A The value type. 15 | * @param {Stream>} fa The input stream. 16 | * @return {Stream} The output stream. 17 | * 18 | * @category filtering 19 | * @__PURE__ 20 | */ 21 | export function compact(fa: Stream>): Stream { 22 | return function* __compact() { 23 | for (const a of fa()) { 24 | if (isSome(a)) { 25 | yield a.value 26 | } 27 | } 28 | } 29 | } 30 | 31 | /** 32 | * Separate a {@link Stream} of {@link Either}s into `Left` and `Right`s, 33 | * creating two new {@link Stream}s where one containing all the left 34 | * values and the other containing all the right values. 35 | * 36 | * @export 37 | * @template E The left value type. 38 | * @template A The right value type. 39 | * @param {Stream>} fa The input stream. 40 | * @return {Separated, Stream>} The separated output streams. 41 | * 42 | * @category filtering 43 | * @__PURE__ 44 | */ 45 | export function separate(fa: Stream>): Separated, Stream> { 46 | return { 47 | *left() { 48 | for (const a of fa()) { 49 | if (isLeft(a)) { 50 | yield a.left 51 | } 52 | } 53 | }, 54 | *right() { 55 | for (const a of fa()) { 56 | if (isRight(a)) { 57 | yield a.right 58 | } 59 | } 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * The `Compactable` category instance for {@link Stream}. 66 | */ 67 | export const Compactable: Compactable1 = { 68 | URI, 69 | compact, 70 | separate, 71 | } 72 | -------------------------------------------------------------------------------- /src/Stream/utils/find-last.ts: -------------------------------------------------------------------------------- 1 | import { flow } from 'fp-ts/lib/function' 2 | import { Option } from 'fp-ts/lib/Option' 3 | import { Predicate } from 'fp-ts/lib/Predicate' 4 | import { Refinement } from 'fp-ts/lib/Refinement' 5 | 6 | import { filter } from '../filterable' 7 | import { Stream } from '../uri' 8 | import { last } from './last' 9 | 10 | /** 11 | * Find the last element which satisfies a predicate (or a refinement) 12 | * function. 13 | * 14 | * It returns an {@link Option} containing the element or `None` if not found. 15 | * 16 | * @export 17 | * @template A The value type. 18 | * @template B The refined value type. 19 | * @param {Refinement} refinement The refinement function. 20 | * @return {(fa: Stream) => Option} A function that takes a stream 21 | * to search. 22 | * 23 | * @__PURE__ 24 | */ 25 | export function findLast(refinement: Refinement): (fa: Stream) => Option 26 | 27 | /** 28 | * Find the last element which satisfies a predicate (or a refinement) 29 | * function. 30 | * 31 | * It returns an {@link Option} containing the element or `None` if not found. 32 | * 33 | * @export 34 | * @template A The value type. 35 | * @param {Predicate} predicate The predicate function. 36 | * @return {(fa: Stream) => Option} A function that takes 37 | * a stream to search. 38 | * 39 | * @__PURE__ 40 | */ 41 | export function findLast(predicate: Predicate): (fa: Stream) => Option 42 | 43 | /** 44 | * Find the last element which satisfies a predicate (or a refinement) 45 | * function. 46 | * 47 | * It returns an {@link Option} containing the element or `None` if not found. 48 | * 49 | * @export 50 | * @template A The value type. 51 | * @param {Predicate} predicate The predicate function. 52 | * @return {(fa: Stream) => Option} A function that takes 53 | * a stream to search. 54 | * 55 | * @__PURE__ 56 | */ 57 | export function findLast(predicate: Predicate): (fa: Stream) => Option 58 | export function findLast(predicate: Predicate): (fa: Stream) => Option { 59 | return flow(filter(predicate), last) 60 | } 61 | -------------------------------------------------------------------------------- /src/Stream/transformations/from-predicate.ts: -------------------------------------------------------------------------------- 1 | import { Predicate } from 'fp-ts/lib/Predicate' 2 | import { Refinement } from 'fp-ts/lib/Refinement' 3 | 4 | import { of } from '../pointed' 5 | import { Stream } from '../uri' 6 | import { empty } from '../zero' 7 | 8 | /** 9 | * Creates a {@link Stream} from a {@link Refinement}. 10 | * 11 | * @export 12 | * @template A The value type. 13 | * @template B The refined value type. 14 | * @param {Refinement} refinement The refinement function. 15 | * @return {(a: A) => Stream} A function that will take a value 16 | * and return a stream of it. 17 | * 18 | * @__PURE__ 19 | */ 20 | export function fromPredicate(refinement: Refinement): (a: A) => Stream 21 | 22 | /** 23 | * Creates a {@link Stream} from a {@link Predicate}. 24 | * 25 | * @export 26 | * @template A The value type. 27 | * @template B The refined value type. 28 | * @param {Predicate} predicate The refinement function. 29 | * @return {(b: B) => Stream} A function that will take a value 30 | * and return a stream of it. 31 | * 32 | * @__PURE__ 33 | */ 34 | export function fromPredicate(predicate: Predicate): (b: B) => Stream 35 | 36 | /** 37 | * Creates a {@link Stream} from a {@link Predicate}. 38 | * 39 | * @export 40 | * @template A The value type. 41 | * @template B The refined value type. 42 | * @param {Predicate} predicate The refinement function. 43 | * @return {(a: A) => Stream} A function that will take a value 44 | * and return a stream of it. 45 | * 46 | * @__PURE__ 47 | */ 48 | export function fromPredicate(predicate: Predicate): (a: A) => Stream 49 | export function fromPredicate(predicate: Predicate) { 50 | /** 51 | * Takes a value to apply the previously given predicate to create a 52 | * {@link Stream} instance. 53 | * 54 | * @param {A} a The input value. 55 | * @return {Stream} The output stream. 56 | * 57 | * @step 1 58 | * @__PURE__ 59 | */ 60 | return function _fromPredicate(a: A): Stream { 61 | if (predicate(a)) return of(a) 62 | else { 63 | return empty 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Stream/alternative.ts: -------------------------------------------------------------------------------- 1 | import { Alternative1 } from 'fp-ts/lib/Alternative' 2 | import { Lazy } from 'fp-ts/lib/function' 3 | 4 | import { Applicative } from './applicative' 5 | import { Functor } from './functor' 6 | import { Pointed } from './pointed' 7 | import { Stream, URI } from './uri' 8 | import { Zero } from './zero' 9 | 10 | /** 11 | * Less strict version of [`alt`](#alt). 12 | * 13 | * The `W` suffix (short for **W**idening) means that the return types will be 14 | * merged. 15 | * 16 | * @export 17 | * @template B The other value type. 18 | * @param {Lazy>} that The lazy function providing the other stream. 19 | * @return {(fa: Stream) => Stream} A function that takes a stream and 20 | * returns another stream whose elements are concatted with the given one. 21 | * 22 | * @category error handling 23 | * @__PURE__ 24 | */ 25 | export function altW(that: Lazy>) { 26 | /** 27 | * Takes a stream to concat the prevoiusly given stream with this one. 28 | * 29 | * @template A The value type. 30 | * @param {Stream} ma The input stream. 31 | * @return {(Stream)} The output stream. 32 | * @step 1 33 | * 34 | * @category error handling 35 | * @__PURE__ 36 | */ 37 | return function _altW(ma: Stream): Stream { 38 | return function* __altW() { 39 | yield* ma() 40 | yield* that()() 41 | } 42 | } 43 | } 44 | 45 | /** 46 | * Concatenates the inputs to a single {@link Stream}. 47 | * 48 | * @export 49 | * @template A The value type. 50 | * @param {Lazy>} that The lazy function providing the other stream. 51 | * @return {(fa: Stream) => Stream} A function that takes a stream and 52 | * returns another stream whose elements are concatted with the given one. 53 | */ 54 | export function alt(that: Lazy>) { 55 | return altW(that) 56 | } 57 | 58 | /** 59 | * The `Alternative` category instance for {@link Stream}. 60 | */ 61 | export const Alternative: Alternative1 = { 62 | URI, 63 | ap: Applicative.ap, 64 | map: Functor.map, 65 | of: Pointed.of, 66 | zero: Zero.zero, 67 | alt(fa, that) { return alt(that)(fa) }, 68 | } 69 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/elem.ts: -------------------------------------------------------------------------------- 1 | import { Eq } from 'fp-ts/lib/Eq' 2 | import { pipe } from 'fp-ts/lib/function' 3 | import { isSome } from 'fp-ts/lib/Option' 4 | import { map as mapTask, Task } from 'fp-ts/lib/Task' 5 | 6 | import { AsyncStream } from '../uri' 7 | import { findFirst } from './find-first' 8 | 9 | /** 10 | * Tests if a value is a member of an {@link AsyncStream}. 11 | * 12 | * Takes a {@link Eq} of `A` as a single argument which returns the function 13 | * to use to search for a value of type `A` in an {@link AsyncStream}. 14 | * 15 | * **Warning: This function consumes the stream.** 16 | * 17 | * @export 18 | * @template A The value type. 19 | * @param {Eq} E The equality instance. 20 | * @return {(a: A) => (fa: AsyncStream) => Task} A function to 21 | * use to search for a value in the async stream. 22 | * 23 | * @__PURE__ 24 | */ 25 | export function elem(E: Eq) { 26 | /** 27 | * Takes an element to test if that is a member of an {@link AsyncStream}. 28 | * 29 | * @param {A} a The value to test if that is in the stream. 30 | * @return {(fa: AsyncStream) => Task} A function that will 31 | * take the async stream to search. 32 | * 33 | * @step 1 34 | * @__PURE__ 35 | */ 36 | function _elem(a: A): (fa: AsyncStream) => Task 37 | 38 | /** 39 | * Takes an element to test if that is a member of an {@link AsyncStream}. 40 | * 41 | * @param {A} a The value to test if that is in the stream. 42 | * @param {AsyncStream} fa The async stream to search the element. 43 | * @return {Task} `true` if the element is a member of the async 44 | * stream, `false` otherwise. 45 | * 46 | * @step 1 47 | * @__PURE__ 48 | */ 49 | function _elem(a: A, fa: AsyncStream): Task 50 | function _elem(a: A, fa?: AsyncStream): ((fa: AsyncStream) => Task) | Task { 51 | if (typeof fa !== 'undefined') { 52 | return pipe(fa, findFirst(_a => E.equals(a, _a)), mapTask(isSome)) 53 | } 54 | 55 | return function __elem(ma: AsyncStream): Task { 56 | return _elem(a, ma) 57 | } 58 | } 59 | 60 | return _elem 61 | } 62 | -------------------------------------------------------------------------------- /src/Stream/utils/comprehension.ts: -------------------------------------------------------------------------------- 1 | import { constTrue } from 'fp-ts/lib/function' 2 | import { ReadonlyNonEmptyArray } from 'fp-ts/lib/ReadonlyNonEmptyArray' 3 | 4 | import { Stream } from '../uri' 5 | 6 | type UnwrapOutputs< 7 | SA extends ReadonlyArray>, 8 | Output extends ReadonlyArray = [] 9 | > = SA extends readonly [ 10 | Stream, 11 | ...infer R extends ReadonlyArray> 12 | ] ? UnwrapOutputs : Output 13 | 14 | type OutputMapper< 15 | T extends ReadonlyArray>, 16 | R 17 | > = (...args: [ ...UnwrapOutputs ]) => R 18 | 19 | type Condition< 20 | T extends ReadonlyArray> 21 | > = OutputMapper 22 | 23 | /** 24 | * {@link Stream} comprehension. 25 | * 26 | * ``` 27 | * { f(x, y, ...) | x ← xs, y ← ys, ..., g(x, y, ...) } 28 | * ``` 29 | * 30 | * @export 31 | * @template R The output type. 32 | * @template I The input parameters array type. 33 | * @param {I} input The input streams. 34 | * @param {OutputMapper} f The output mapper function. 35 | * @param {Condition} [g] Optional condition function. 36 | * @return {Stream} The output stream. 37 | * 38 | * @__PURE__ 39 | */ 40 | export function comprehension>>( 41 | input: I, 42 | f: OutputMapper, 43 | g?: Condition 44 | ): Stream { 45 | return function* _comprehension() { 46 | g ??= constTrue 47 | 48 | function* go( 49 | mas: Stream[], 50 | collected: Array, 51 | depth = 0 52 | ): Generator { 53 | 54 | if (mas.length === 1) { 55 | const ma = mas[ 0 ] 56 | for (const a of ma()) { 57 | collected[ depth ] = a 58 | if (g!(...(collected as any))) { 59 | yield f(...(collected as any)) 60 | } 61 | } 62 | } 63 | else { 64 | const ma = mas.shift()! 65 | for (const a of ma()) { 66 | collected[ depth ] = a 67 | yield* go(mas, collected, depth + 1) 68 | } 69 | } 70 | } 71 | 72 | yield* go([ ...input ], new Array(input.length)) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Stream/utils/find-first.ts: -------------------------------------------------------------------------------- 1 | import { flow } from 'fp-ts/lib/function' 2 | import { Option } from 'fp-ts/lib/Option' 3 | import { not, Predicate } from 'fp-ts/lib/Predicate' 4 | import { Refinement } from 'fp-ts/lib/Refinement' 5 | 6 | import { Stream } from '../uri' 7 | import { dropLeftWhile } from './drop-left-while' 8 | import { head } from './head' 9 | 10 | /** 11 | * Find the first element which satisfies a predicate (or a refinement) 12 | * function. 13 | * 14 | * It returns an {@link Option} containing the element or `None` if not found. 15 | * 16 | * @export 17 | * @template A The value type. 18 | * @template B The refined value type. 19 | * @param {Refinement} refinement The refinement function. 20 | * @return {(fa: Stream) => Option} A function that takes a stream 21 | * to search. 22 | * 23 | * @__PURE__ 24 | */ 25 | export function findFirst(refinement: Refinement): (fa: Stream) => Option 26 | 27 | /** 28 | * Find the first element which satisfies a predicate (or a refinement) 29 | * function. 30 | * 31 | * It returns an {@link Option} containing the element or `None` if not found. 32 | * 33 | * @export 34 | * @template A The value type. 35 | * @param {Predicate} predicate The predicate function. 36 | * @return {(fa: Stream) => Option} A function that takes 37 | * a stream to search. 38 | * 39 | * @__PURE__ 40 | */ 41 | export function findFirst(predicate: Predicate): (fb: Stream) => Option 42 | 43 | /** 44 | * Find the first element which satisfies a predicate (or a refinement) 45 | * function. 46 | * 47 | * It returns an {@link Option} containing the element or `None` if not found. 48 | * 49 | * @export 50 | * @template A The value type. 51 | * @param {Predicate} predicate The predicate function. 52 | * @return {(fa: Stream) => Option} A function that takes 53 | * a stream to search. 54 | * 55 | * @__PURE__ 56 | */ 57 | export function findFirst(predicate: Predicate): (fa: Stream) => Option 58 | export function findFirst(predicate: Predicate): (fa: Stream) => Option { 59 | return flow(dropLeftWhile(not(predicate)), head) 60 | } 61 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/union.ts: -------------------------------------------------------------------------------- 1 | import { Eq } from 'fp-ts/lib/Eq' 2 | 3 | import { AsyncStream } from '../uri' 4 | 5 | /** 6 | * Creates an {@link AsyncStream} of unique values, in order, from all given 7 | * {@link AsyncStream}s using a {@link Eq} for equality comparisons. 8 | * 9 | * @export 10 | * @template A The value type. 11 | * @param {Eq} E The equality instance. 12 | * @return {(xs: AsyncStream) => (ys: AsyncStream) => AsyncStream} A 13 | * function that takes async streams. 14 | * 15 | * @__PURE__ 16 | */ 17 | export function union(E: Eq) { 18 | /** 19 | * Takes an {@link AsyncStream} to union with another {@link AsyncStream}. 20 | * 21 | * @param {AsyncStream} xs The first async stream. 22 | * @return {(ys: AsyncStream) => AsyncStream} A function that takes the 23 | * other async stream. 24 | * 25 | * @step 1 26 | * @__PURE__ 27 | */ 28 | function _union(xs: AsyncStream): (ys: AsyncStream) => AsyncStream 29 | 30 | /** 31 | * Takes an {@link AsyncStream} to union with another {@link AsyncStream}. 32 | * 33 | * @param {AsyncStream} xs The first async stream. 34 | * @param {AsyncStream} ys The second async stream. 35 | * @return {AsyncStream} A new async stream containing unique values from 36 | * both async streams. 37 | * 38 | * @step 1 39 | * @__PURE__ 40 | */ 41 | function _union(xs: AsyncStream, ys: AsyncStream): AsyncStream 42 | function _union(xs: AsyncStream, ys?: AsyncStream): AsyncStream | ((ys: AsyncStream) => AsyncStream) { 43 | if (typeof ys !== 'undefined') { 44 | return async function* __union() { 45 | const collected: A[] = [] 46 | for await (const y of ys()) { 47 | yield y 48 | collected.push(y) 49 | } 50 | 51 | for await (const x of xs()) { 52 | if (!collected.some(it => E.equals(it, x))) { 53 | yield x 54 | collected.push(x) 55 | } 56 | } 57 | } 58 | } 59 | 60 | return function _union_union(my: AsyncStream): AsyncStream { 61 | return _union(xs, my) 62 | } 63 | } 64 | 65 | return _union 66 | } 67 | -------------------------------------------------------------------------------- /src/AsyncStream/applicative.ts: -------------------------------------------------------------------------------- 1 | import { Applicative1 } from 'fp-ts/lib/Applicative' 2 | 3 | import { toArray } from './conversions' 4 | import { Functor } from './functor' 5 | import { Pointed } from './pointed' 6 | import { AsyncStream, URI } from './uri' 7 | import { MaybeAsync } from './utils/maybe-async' 8 | 9 | /** 10 | * Applies a {@link AsyncStream} of type `A` to {@link AsyncStream} of functions 11 | * from `A` to `B`. 12 | * 13 | * @export 14 | * @template A The value type. 15 | * @param {AsyncStream} fa The source async stream. 16 | * @return {(fab: AsyncStream<(a: A) => B>) => AsyncStream} A function that 17 | * takes a {@link AsyncStream} of functions. 18 | * 19 | * @__PURE__ 20 | */ 21 | export function ap(fa: AsyncStream) { 22 | /** 23 | * Applies previously given {@link AsyncStream} to the 24 | * given {@link AsyncStream} of functions that take the items of the 25 | * previously given {@link AsyncStream} and returns `B` values. 26 | * 27 | * @template B The output value type. 28 | * @param {AsyncStream<(a: A) => B | Promise>} fab The async function 29 | * stream to apply the values of the previously given async stream to its 30 | * functions returning `B` value. 31 | * 32 | * @return {AsyncStream} The output async stream. 33 | * @step 1 34 | * 35 | * @__PURE__ 36 | */ 37 | return function _ap(fab: AsyncStream<(a: A) => MaybeAsync>): AsyncStream { 38 | return async function* __ap() { 39 | const as$ = toArray(fa) 40 | const fabs$ = toArray(fab) 41 | 42 | const [ as, fabs ] = await Promise.all([ as$, fabs$ ]) 43 | for (const a of as) { 44 | for (const fab of fabs) { 45 | yield await fab(a) 46 | } 47 | } 48 | } 49 | } 50 | } 51 | 52 | /** 53 | * The `Applicative` category instance for {@link AsyncStream}. 54 | * 55 | * Same with {@link ApplicativePar} 56 | */ 57 | export const Applicative: Applicative1 = { 58 | URI, 59 | map: Functor.map, 60 | of: Pointed.of, 61 | ap(fab, fa) { return ap(fa)(fab) } 62 | } 63 | 64 | /** 65 | * The `ApplicativePar` category instance for {@link AsyncStream}. 66 | */ 67 | export const ApplicativePar = Applicative 68 | -------------------------------------------------------------------------------- /src/AsyncStream/compactable.ts: -------------------------------------------------------------------------------- 1 | import { Compactable1 } from 'fp-ts/lib/Compactable' 2 | import { Either, isLeft, isRight } from 'fp-ts/lib/Either' 3 | import { isSome, Option } from 'fp-ts/lib/Option' 4 | import { Separated } from 'fp-ts/lib/Separated' 5 | 6 | import { AsyncStream, URI } from './uri' 7 | 8 | /** 9 | * Compact a {@link AsyncStream} of {@link Option}s discarding the `None` values 10 | * and keeping the `Some` values. It returns a new {@link AsyncStream} 11 | * containing the values of `Some` options. 12 | * 13 | * @export 14 | * @template A The value type. 15 | * @param {AsyncStream>} fa The input async stream. 16 | * @return {AsyncStream} The output async stream. 17 | * 18 | * @category filtering 19 | * @__PURE__ 20 | */ 21 | export function compact(fa: AsyncStream>): AsyncStream { 22 | return async function* __compact() { 23 | for await (const a of fa()) { 24 | if (isSome(a)) { 25 | yield a.value 26 | } 27 | } 28 | } 29 | } 30 | 31 | /** 32 | * Separate a {@link AsyncStream} of {@link Either}s into `Left` and `Right`s, 33 | * creating two new {@link AsyncStream}s where one containing all the left 34 | * values and the other containing all the right values. 35 | * 36 | * @export 37 | * @template E The left value type. 38 | * @template A The right value type. 39 | * @param {AsyncStream>} fa The input async stream. 40 | * @return {Separated, AsyncStream>} The separated output 41 | * async streams. 42 | * 43 | * @category filtering 44 | * @__PURE__ 45 | */ 46 | export function separate(fa: AsyncStream>): Separated, AsyncStream> { 47 | return { 48 | async *left() { 49 | for await (const a of fa()) { 50 | if (isLeft(a)) { 51 | yield a.left 52 | } 53 | } 54 | }, 55 | async *right() { 56 | for await (const a of fa()) { 57 | if (isRight(a)) { 58 | yield a.right 59 | } 60 | } 61 | } 62 | } 63 | } 64 | 65 | /** 66 | * The `Compactable` category instance for {@link AsyncStream}. 67 | */ 68 | export const Compactable: Compactable1 = { 69 | URI, 70 | compact, 71 | separate, 72 | } 73 | -------------------------------------------------------------------------------- /src/Stream/utils/min-max.ts: -------------------------------------------------------------------------------- 1 | import { none, Option, some } from 'fp-ts/Option' 2 | 3 | import type { Ord } from 'fp-ts/Ord' 4 | 5 | import type { Stream } from "../uri"; 6 | 7 | /** 8 | * Gets the minimum value from a {@link Stream}. 9 | * 10 | * @export 11 | * @template A The value type. 12 | * @param {Ord} ord The {@link Ord} instance of the values. 13 | * @return {(xs: Stream) => Option} A function that takes a stream to 14 | * extract the minimum value. 15 | * 16 | * @__PURE__ 17 | */ 18 | export function minimum(ord: Ord) { 19 | /** 20 | * Gets the minimum value from a {@link Stream}. 21 | * 22 | * @step 1 23 | * @template A The value type. 24 | * @param {Stream} xs The stream instance to find its minimum value. 25 | * @return {Option} The minimum value found in the stream. 26 | * 27 | * @__PURE__ 28 | */ 29 | return function _minimum(xs: Stream): Option { 30 | let gen = xs() 31 | 32 | let { value: lhs, done } = gen.next() 33 | if (done) return none 34 | 35 | for (const rhs of gen) { 36 | if (ord.compare(lhs, rhs) === 1) { 37 | lhs = rhs 38 | } 39 | } 40 | 41 | return some(lhs) 42 | } 43 | } 44 | 45 | /** 46 | * Gets the maximum value from a {@link Stream}. 47 | * 48 | * @export 49 | * @template A The value type. 50 | * @param {Ord} ord The {@link Ord} instance of the values. 51 | * @return {(xs: Stream) => Option} A function that takes a stream to 52 | * extract the maximum value. 53 | * 54 | * @__PURE__ 55 | */ 56 | export function maximum(ord: Ord) { 57 | /** 58 | * Gets the maximum value from a {@link Stream}. 59 | * 60 | * @step 1 61 | * @template A The value type. 62 | * @param {Stream} xs The stream instance to find its maximum value. 63 | * @return {Option} The maximum value found in the stream. 64 | * 65 | * @__PURE__ 66 | */ 67 | return function _maximum(xs: Stream): Option { 68 | let gen = xs() 69 | 70 | let { value: lhs, done } = gen.next() 71 | if (done) return none 72 | 73 | for (const rhs of gen) { 74 | if (ord.compare(lhs, rhs) === -1) { 75 | lhs = rhs 76 | } 77 | } 78 | 79 | return some(lhs) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Stream/utils/difference.ts: -------------------------------------------------------------------------------- 1 | import { Eq } from 'fp-ts/lib/Eq' 2 | 3 | import { toArray } from '../conversions' 4 | import { Stream } from '../uri' 5 | 6 | /** 7 | * Creates a {@link Stream} of {@link Stream} values not included in the other 8 | * given {@link Stream} using a {@link Eq} for equality comparisons. 9 | * 10 | * The order and references of result values are determined by the first 11 | * {@link Stream}. 12 | * 13 | * @export 14 | * @template A The value type. 15 | * @param {Eq} E The equality instance. 16 | * @return {(xs: Stream) => (ys: Stream) => Stream} A function that 17 | * takes a stream to provide the differences. 18 | * 19 | * @__PURE__ 20 | */ 21 | export function difference(E: Eq) { 22 | /** 23 | * Takes a {@link Stream} and returns another {@link Stream} instance to 24 | * create a {@link Stream} of differences. 25 | * 26 | * @param {Stream} xs The stream of values. 27 | * @return {(ys: Stream) => Stream} A function that takes a stream 28 | * to exclude the items of it from the given stream. 29 | * 30 | * @step 1 31 | * @__PURE__ 32 | */ 33 | function _difference(xs: Stream): (ys: Stream) => Stream 34 | 35 | /** 36 | * Takes a {@link Stream} and returns another {@link Stream} instance to 37 | * create a {@link Stream} of differences. 38 | * 39 | * @param {Stream} xs The stream of values. 40 | * @param {Stream} ys The other stream of values. 41 | * @return {Stream} A stream of the values that are not found in the 42 | * second stream. 43 | * 44 | * @step 1 45 | * @__PURE__ 46 | */ 47 | function _difference(xs: Stream, ys: Stream): Stream 48 | function _difference(xs: Stream, ys?: Stream): Stream | ((ys: Stream) => Stream) { 49 | if (typeof ys !== 'undefined') { 50 | return function* __difference() { 51 | const xList = toArray(xs) 52 | 53 | for (const y of ys()) { 54 | if (!xList.some(it => E.equals(it, y))) { 55 | yield y 56 | } 57 | } 58 | } 59 | } 60 | 61 | return function _difference_difference(my: Stream): Stream { 62 | return _difference(xs, my) 63 | } 64 | } 65 | 66 | return _difference 67 | } 68 | -------------------------------------------------------------------------------- /src/Stream/utils/match-left.ts: -------------------------------------------------------------------------------- 1 | import { Lazy } from 'fp-ts/lib/function' 2 | 3 | import { Stream } from '../uri' 4 | 5 | /** 6 | * Less strict version of {@link matchLeft}. 7 | * 8 | * @export 9 | * @template B The value type on empty. 10 | * @template A The value type on non empty. 11 | * @template C The result type of the non empty function. 12 | * @param {Lazy} onEmpty The lazy function that will be executed when the 13 | * stream is empty. 14 | * 15 | * @param {(head: A, tail: Stream) => C} onNonEmpty The function that will 16 | * be executed when the stream is not empty. 17 | * 18 | * @return {(fa: Stream) => B | C} A function that takes a stream. 19 | * 20 | * @category pattern matching 21 | * @__PURE__ 22 | */ 23 | export function matchLeftW(onEmpty: Lazy, onNonEmpty: (head: A, tail: Stream) => C) { 24 | /** 25 | * Takes a {@link Stream} and returns the result value of the previously 26 | * given handler functions. 27 | * 28 | * @param {Stream} fa The input stream. 29 | * @return {(B | C)} The output value. 30 | * 31 | * @step 1 32 | * @category pattern matching 33 | * @__PURE__ 34 | */ 35 | return function _matchLeftW(fa: Stream): B | C { 36 | const gen = fa() 37 | const { value: head, done } = gen.next() 38 | 39 | if (done) { 40 | return onEmpty() 41 | } 42 | else { 43 | return onNonEmpty(head, function* _tail() { yield* gen }) 44 | } 45 | } 46 | } 47 | 48 | /** 49 | * Break a {@link Stream} into its first element and remaining elements. 50 | * 51 | * @export 52 | * @template B The value type on empty. 53 | * @template A The value type on non empty. 54 | * @param {Lazy} onEmpty The lazy function that will be executed when the 55 | * stream is empty. 56 | * 57 | * @param {(head: A, tail: Stream) => B} onNonEmpty The function that will 58 | * be executed when the stream is not empty. 59 | * 60 | * @return {(fa: Stream) => B} A function that takes a stream. 61 | * 62 | * @category pattern matching 63 | * @__PURE__ 64 | */ 65 | export function matchLeft(onEmpty: Lazy, onNonEmpty: (head: A, tail: Stream) => B) { 66 | return matchLeftW(onEmpty, onNonEmpty) 67 | } 68 | 69 | /** 70 | * Alias for {@link matchLeft}. 71 | */ 72 | export const foldLeft = matchLeft 73 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/match.ts: -------------------------------------------------------------------------------- 1 | import { Lazy } from 'fp-ts/lib/function' 2 | import { Task } from 'fp-ts/lib/Task' 3 | 4 | import { AsyncStream } from '../uri' 5 | import { isEmpty } from './is-empty' 6 | 7 | /** 8 | * Less strict version of {@link match}. 9 | * 10 | * @export 11 | * @template B The type of the value on empty. 12 | * @template A The type of the streamed value. 13 | * @template C The type of the value on non-empty. 14 | * @param {Lazy | Task} onEmpty The lazy function that will be executed 15 | * if the async stream is empty. 16 | * 17 | * @param {(fa: AsyncStream) => C | Promise} onNonEmpty The function that 18 | * will be executed if the async stream is not empty. 19 | * 20 | * @return {(fa: AsyncStream) => Task} A function that takes an 21 | * async stream to match. 22 | * 23 | * @category pattern matching 24 | * @__PURE__ 25 | */ 26 | export function matchW(onEmpty: Lazy | Task, onNonEmpty: (fa: AsyncStream) => C | Promise) { 27 | /** 28 | * Takes an {@link AsyncStream} to match. 29 | * 30 | * @param {AsyncStream} fa The async stream. 31 | * @return {Task} The match value. 32 | * 33 | * @category pattern matching 34 | * @__PURE__ 35 | */ 36 | return function _matchW(fa: AsyncStream): Task { 37 | return async function __matchW() { 38 | if (await isEmpty(fa)()) { 39 | return await onEmpty() 40 | } 41 | else { 42 | return await onNonEmpty(fa) 43 | } 44 | } 45 | } 46 | } 47 | 48 | /** 49 | * Matches an {@link AsyncStream} whether if that was empty or not. 50 | * 51 | * @export 52 | * @template B The type of the value on empty. 53 | * @template A The type of the streamed value. 54 | * @param {Lazy | Task} onEmpty The lazy function that will be executed 55 | * if the async stream is empty. 56 | * 57 | * @param {(fa: AsyncStream) => Promise} onNonEmpty The function that will 58 | * be executed. 59 | * 60 | * @return {(fa: AsyncStream) => Promise} A function that takes an 61 | * async stream to match. 62 | * 63 | * @category pattern matching 64 | * @__PURE__ 65 | */ 66 | export function match(onEmpty: Lazy | Task, onNonEmpty: (fa: AsyncStream) => B | Promise) { 67 | return matchW(onEmpty, onNonEmpty) 68 | } 69 | -------------------------------------------------------------------------------- /src/Stream/utils/intersection.ts: -------------------------------------------------------------------------------- 1 | import { Eq } from 'fp-ts/lib/Eq' 2 | 3 | import { toArray } from '../conversions' 4 | import { Stream } from '../uri' 5 | 6 | /** 7 | * Creates a {@link Stream} of unique values that included in all given 8 | * {@link Stream} using a {@link Eq} for equality comparisons. 9 | * 10 | * The order and references of result values are determined by the first 11 | * {@link Stream}. 12 | * 13 | * @export 14 | * @template A The value type. 15 | * @param {Eq} E The equality comparer instance. 16 | * @return {(xs: Stream) => (ys: Stream) => Stream} A function that 17 | * takes a stream to modify. 18 | * 19 | * @__PURE__ 20 | */ 21 | export function intersection(E: Eq) { 22 | /** 23 | * Takes a {@link Stream} to get the intersection between that and the 24 | * other. 25 | * 26 | * @param {Stream} xs The first stream. 27 | * @return {(ys: Stream) => Stream} A function that takes the 28 | * other stream. 29 | * 30 | * @step 1 31 | * @__PURE__ 32 | */ 33 | function _intersection(xs: Stream): (ys: Stream) => Stream 34 | 35 | /** 36 | * Takes a {@link Stream} to get the intersection between that and the 37 | * other. 38 | * 39 | * @param {Stream} xs The first stream. 40 | * @param {Stream} ys The second stream. 41 | * @return {Stream} A stream containing the intersections of the given 42 | * streams. 43 | * 44 | * @step 1 45 | * @__PURE__ 46 | */ 47 | function _intersection(xs: Stream, ys: Stream): Stream 48 | function _intersection(xs: Stream, ys?: Stream): Stream | ((ys: Stream) => Stream) { 49 | if (typeof ys !== 'undefined') { 50 | return function* _intersection() { 51 | const genX = xs() 52 | let result: IteratorResult = genX.next() 53 | if (result.done) return 54 | 55 | const collected = toArray(ys) 56 | 57 | while (!result.done) { 58 | const value = result.value 59 | 60 | if (collected.some(it => E.equals(it, value))) { 61 | yield value 62 | } 63 | 64 | result = genX.next() 65 | } 66 | } 67 | } 68 | 69 | return function _intersection_intersection(my: Stream): Stream { 70 | return _intersection(xs, my) 71 | } 72 | } 73 | 74 | return _intersection 75 | } 76 | -------------------------------------------------------------------------------- /src/AsyncStream/alternative.ts: -------------------------------------------------------------------------------- 1 | import { Alternative1 } from 'fp-ts/lib/Alternative' 2 | import { Lazy } from 'fp-ts/lib/function' 3 | 4 | import { Applicative } from './applicative' 5 | import { Functor } from './functor' 6 | import { Pointed } from './pointed' 7 | import { AsyncStream, URI } from './uri' 8 | import { Zero } from './zero' 9 | 10 | /** 11 | * Less strict version of [`alt`](#alt). 12 | * 13 | * The `W` suffix (short for **W**idening) means that the return types will be 14 | * merged. 15 | * 16 | * @export 17 | * @template B The other value type. 18 | * @param {Lazy>} that The lazy function providing the other 19 | * async stream. 20 | * 21 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes 22 | * an async stream and returns another stream whose elements are concatted with 23 | * the given one. 24 | * 25 | * @category error handling 26 | * @__PURE__ 27 | */ 28 | export function altW(that: Lazy>) { 29 | /** 30 | * Takes an async stream to concat the prevoiusly given async stream with 31 | * this one. 32 | * 33 | * @template A The value type. 34 | * @param {AsyncStream} ma The input async stream. 35 | * @return {(AsyncStream)} The output async stream. 36 | * @step 1 37 | * 38 | * @category error handling 39 | * @__PURE__ 40 | */ 41 | return function _altW(ma: AsyncStream): AsyncStream { 42 | return async function* __altW() { 43 | yield* ma() 44 | yield* that()() 45 | } 46 | } 47 | } 48 | 49 | /** 50 | * Concatenates the inputs to a single {@link AsyncStream}. 51 | * 52 | * @export 53 | * @template A The value type. 54 | * @param {Lazy>} that The lazy function providing the other 55 | * async stream. 56 | * 57 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 58 | * async stream and returns another async stream whose elements are concatted 59 | * with the given one. 60 | */ 61 | export function alt(that: Lazy>) { 62 | return altW(that) 63 | } 64 | 65 | /** 66 | * The `Alternative` category instance for {@link AsyncStream}. 67 | */ 68 | export const Alternative: Alternative1 = { 69 | URI, 70 | ap: Applicative.ap, 71 | map: Functor.map, 72 | of: Pointed.of, 73 | zero: Zero.zero, 74 | alt(fa, that) { return alt(that)(fa) }, 75 | } 76 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/comprehension.ts: -------------------------------------------------------------------------------- 1 | import { constTrue } from 'fp-ts/lib/function' 2 | import { ReadonlyNonEmptyArray } from 'fp-ts/lib/ReadonlyNonEmptyArray' 3 | 4 | import { AsyncStream } from '../uri' 5 | import { MaybeAsync } from './maybe-async' 6 | 7 | type UnwrapOutputs< 8 | SA extends ReadonlyArray>, 9 | Output extends ReadonlyArray = [] 10 | > = SA extends readonly [ 11 | AsyncStream, 12 | ...infer R extends ReadonlyArray> 13 | ] ? UnwrapOutputs : Output 14 | 15 | type OutputMapper< 16 | T extends ReadonlyArray>, 17 | R 18 | > = (...args: [ ...UnwrapOutputs ]) => MaybeAsync 19 | 20 | type Condition< 21 | T extends ReadonlyArray> 22 | > = OutputMapper 23 | 24 | /** 25 | * {@link AsyncStream} comprehension. 26 | * 27 | * ``` 28 | * { f(x, y, ...) | x ← xs, y ← ys, ..., g(x, y, ...) } 29 | * ``` 30 | * 31 | * @export 32 | * @template R The output type. 33 | * @template I The input parameters array type. 34 | * @param {I} input The input streams. 35 | * @param {OutputMapper} f The output mapper function. 36 | * @param {Condition} [g] Optional condition function. 37 | * @return {AsyncStream} The output async stream. 38 | * 39 | * @__PURE__ 40 | */ 41 | export function comprehension>>( 42 | input: I, 43 | f: OutputMapper, 44 | g?: Condition 45 | ): AsyncStream { 46 | return async function* _comprehension() { 47 | g ??= constTrue 48 | 49 | async function* go( 50 | mas: AsyncStream[], 51 | collected: Array, 52 | depth = 0 53 | ): AsyncGenerator { 54 | 55 | if (mas.length === 1) { 56 | const ma = mas[ 0 ] 57 | for await (const a of ma()) { 58 | collected[ depth ] = a 59 | const condition = await g!(...(collected as any)) 60 | if (condition) { 61 | yield await f(...(collected as any)) 62 | } 63 | } 64 | } 65 | else { 66 | const ma = mas.shift()! 67 | for await (const a of ma()) { 68 | collected[ depth ] = a 69 | yield* go(mas, collected, depth + 1) 70 | } 71 | } 72 | } 73 | 74 | yield* go([ ...input ], new Array(input.length)) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/find-last.ts: -------------------------------------------------------------------------------- 1 | import { flow } from 'fp-ts/lib/function' 2 | import { Option } from 'fp-ts/lib/Option' 3 | import { Refinement } from 'fp-ts/lib/Refinement' 4 | import { Task } from 'fp-ts/lib/Task' 5 | 6 | import { filter } from '../filterable' 7 | import { AsyncStream } from '../uri' 8 | import { AsyncPredicate } from './async-predicate' 9 | import { last } from './last' 10 | 11 | /** 12 | * Find the last element which satisfies a predicate (or a refinement) 13 | * function. 14 | * 15 | * It returns a {@link Task} of an {@link Option} containing the element 16 | * or `None` if not found. 17 | * 18 | * @export 19 | * @template A The value type. 20 | * @template B The refined value type. 21 | * @param {Refinement} refinement The refinement function. 22 | * @return {(fa: AsyncStream) => Task>} A function that takes an 23 | * async stream to search. 24 | * 25 | * @__PURE__ 26 | */ 27 | export function findLast(refinement: Refinement): (fa: AsyncStream) => Task> 28 | 29 | /** 30 | * Find the last element which satisfies a predicate (or a refinement) 31 | * function. 32 | * 33 | * It returns a {@link Task} of an {@link Option} containing the element or 34 | * `None` if not found. 35 | * 36 | * @export 37 | * @template A The value type. 38 | * @param {AsyncPredicate} predicate The predicate function. 39 | * @return {(fa: AsyncStream) => Task>} A function 40 | * that takes an async stream to search. 41 | * 42 | * @__PURE__ 43 | */ 44 | export function findLast(predicate: AsyncPredicate): (fa: AsyncStream) => Task> 45 | 46 | /** 47 | * Find the last element which satisfies a predicate (or a refinement) 48 | * function. 49 | * 50 | * It returns a {@link Task} of an {@link Option} containing the element 51 | * or `None` if not found. 52 | * 53 | * @export 54 | * @template A The value type. 55 | * @param {AsyncPredicate} predicate The predicate function. 56 | * @return {(fa: AsyncStream) => Task>} A function that takes 57 | * an async stream to search. 58 | * 59 | * @__PURE__ 60 | */ 61 | export function findLast(predicate: AsyncPredicate): (fa: AsyncStream) => Task> 62 | export function findLast(predicate: AsyncPredicate): (fa: AsyncStream) => Task> { 63 | return flow(filter(predicate), last) 64 | } 65 | -------------------------------------------------------------------------------- /src/Stream/utils/drop-left-while.ts: -------------------------------------------------------------------------------- 1 | import { Predicate } from 'fp-ts/lib/Predicate' 2 | import { Refinement } from 'fp-ts/lib/Refinement' 3 | 4 | import { Stream } from '../uri' 5 | 6 | /** 7 | * Creates a new {@link Stream} which is a copy of the input dropping the 8 | * longest initial substream for which all element satisfy the specified 9 | * predicate/refinement. 10 | * 11 | * @export 12 | * @template A The value type. 13 | * @template B The refined value type. 14 | * @param {Refinement} refinement The refinement/predicate function. 15 | * @return {(fa: Stream) => Stream} A function that takes a stream 16 | * to drop its left while the condition holds. 17 | * 18 | * @__PURE__ 19 | */ 20 | export function dropLeftWhile( 21 | refinement: Refinement 22 | ): (fa: Stream) => Stream 23 | 24 | /** 25 | * Creates a new {@link Stream} which is a copy of the input dropping the 26 | * longest initial substream for which all element satisfy the specified 27 | * predicate/refinement. 28 | * 29 | * @export 30 | * @template A The value type. 31 | * @param {Predicate} predicate The refinement/predicate function. 32 | * @return {(fa: Stream) => Stream} A function that takes a 33 | * stream to drop its left while the condition holds. 34 | * 35 | * @__PURE__ 36 | */ 37 | export function dropLeftWhile( 38 | predicate: Predicate 39 | ): (fb: Stream) => Stream 40 | 41 | /** 42 | * Creates a new {@link Stream} which is a copy of the input dropping the 43 | * longest initial substream for which all element satisfy the specified 44 | * predicate/refinement. 45 | * 46 | * @export 47 | * @template A The value type. 48 | * @param {Predicate} predicate The refinement/predicate function. 49 | * @return {(fa: Stream) => Stream} A function that takes a 50 | * stream to drop its left while the condition holds. 51 | * 52 | * @__PURE__ 53 | */ 54 | export function dropLeftWhile(predicate: Predicate): (fa: Stream) => Stream 55 | export function dropLeftWhile(predicate: Predicate): (fa: Stream) => Stream { 56 | return function _dropLeftWhile(fa) { 57 | return function* __dropLeftWhile() { 58 | const gen = fa() 59 | 60 | for (const a of gen) { 61 | if (predicate(a)) { 62 | continue 63 | } 64 | 65 | yield a 66 | yield* gen 67 | return 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Stream/utils/zip.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from 'fp-ts/lib/function' 2 | 3 | import { fromIterable } from '../conversions' 4 | import { map } from '../functor' 5 | import { Stream } from '../uri' 6 | import { zipArray } from './zip-array' 7 | 8 | /** 9 | * Takes two {@link Stream}s and returns a {@link Stream} of corresponding 10 | * pairs as {@link Stream}s yielding single elements. 11 | * 12 | * @export 13 | * @template B The value type. 14 | * @param {Stream} fb The input stream. 15 | * @return {((fa: Stream) => Stream>)} A function 16 | * that takes another stream to zip. 17 | * 18 | * @see {@link zipArray} 19 | * 20 | * @__PURE__ 21 | */ 22 | export function zip(fb: Stream): (fa: Stream) => Stream> 23 | 24 | /** 25 | * Takes two {@link Stream}s and returns a {@link Stream} of corresponding 26 | * pairs as {@link Stream}s yielding single elements. 27 | * 28 | * @export 29 | * @template A The value of the first stream type. 30 | * @template B The value of the second stream type. 31 | * @param {Stream} fa The first stream. 32 | * @param {Stream} fb The second stream. 33 | * @return {Stream>)} The output stream. 34 | * 35 | * @see {@link zipArray} 36 | * 37 | * @__PURE__ 38 | */ 39 | export function zip(fa: Stream, fb: Stream): Stream> 40 | 41 | export function zip( 42 | fAOrB: Stream, 43 | fb?: Stream 44 | ): Stream> | ((ma: Stream) => Stream>) { 45 | if (typeof fb !== 'undefined') { 46 | return pipe( 47 | zipArray(fAOrB, fb), 48 | map(fromIterable) 49 | ) 50 | } 51 | 52 | return function _zip(fa: Stream): Stream> { 53 | return zip(fa, fAOrB as Stream) 54 | } 55 | } 56 | 57 | /** 58 | * Reverse of {@link zip}. 59 | * 60 | * Takes a {@link Stream} of pairs and returns another {@link Stream} instance 61 | * yielding from both {@link Stream}s in-order. 62 | * 63 | * @export 64 | * @template A The value type of the first stream. 65 | * @template B The value type of the second stream. 66 | * @param {(Stream | Stream>)} mma The input stream of streams. 67 | * @return {(Stream)} A stream of elements from both streams. 68 | * 69 | * @__PURE__ 70 | */ 71 | export function unzip(mma: Stream | Stream>): Stream { 72 | return function* _unzip() { 73 | for (const ma of mma()) { 74 | yield* ma() 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/difference.ts: -------------------------------------------------------------------------------- 1 | import { Eq } from 'fp-ts/lib/Eq' 2 | 3 | import { toArray } from '../conversions' 4 | import { AsyncStream } from '../uri' 5 | 6 | /** 7 | * Creates an {@link AsyncStream} of {@link AsyncStream} values not included in 8 | * the other given {@link AsyncStream} using a {@link Eq} for equality 9 | * comparisons. 10 | * 11 | * The order and references of result values are determined by the first 12 | * {@link AsyncStream}. 13 | * 14 | * @export 15 | * @template A The value type. 16 | * @param {Eq} E The equality instance. 17 | * @return {(xs: AsyncStream) => (ys: AsyncStream) => AsyncStream} A 18 | * function that takes an async stream to provide the differences. 19 | * 20 | * @__PURE__ 21 | */ 22 | export function difference(E: Eq) { 23 | /** 24 | * Takes an {@link AsyncStream} and returns another {@link AsyncStream} 25 | * instance to create an {@link AsyncStream} of differences. 26 | * 27 | * @param {AsyncStream} xs The asyc stream of values. 28 | * @return {(ys: AsyncStream) => AsyncStream} A function that takes an 29 | * async stream to exclude the items of it from the given stream. 30 | * 31 | * @step 1 32 | * @__PURE__ 33 | */ 34 | function _difference(xs: AsyncStream): (ys: AsyncStream) => AsyncStream 35 | 36 | /** 37 | * Takes an {@link AsyncStream} and returns another {@link AsyncStream} 38 | * instance to create an {@link AsyncStream} of differences. 39 | * 40 | * @param {AsyncStream} xs The async stream of values. 41 | * @param {AsyncStream} ys The other async stream of values. 42 | * @return {AsyncStream} An async stream of the values that are not found 43 | * in the second async stream. 44 | * 45 | * @step 1 46 | * @__PURE__ 47 | */ 48 | function _difference(xs: AsyncStream, ys: AsyncStream): AsyncStream 49 | function _difference(xs: AsyncStream, ys?: AsyncStream): AsyncStream | ((ys: AsyncStream) => AsyncStream) { 50 | if (typeof ys !== 'undefined') { 51 | return async function* __difference() { 52 | const xList = await toArray(xs) 53 | 54 | for await (const y of ys()) { 55 | if (!xList.some(it => E.equals(it, y))) { 56 | yield y 57 | } 58 | } 59 | } 60 | } 61 | 62 | return function _difference_difference(my: AsyncStream): AsyncStream { 63 | return _difference(xs, my) 64 | } 65 | } 66 | 67 | return _difference 68 | } 69 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/min-max.ts: -------------------------------------------------------------------------------- 1 | import { TaskOption } from 'fp-ts/lib/TaskOption' 2 | import { none, some } from 'fp-ts/Option' 3 | 4 | import type { Ord } from 'fp-ts/Ord' 5 | 6 | import type { AsyncStream } from "../uri"; 7 | 8 | /** 9 | * Gets the minimum value from a {@link AsyncStream}. 10 | * 11 | * @export 12 | * @template A The value type. 13 | * @param {Ord} ord The {@link Ord} instance of the values. 14 | * @return {(xs: AsyncStream) => TaskOption} A function that takes a stream to 15 | * extract the minimum value. 16 | * 17 | * @__PURE__ 18 | */ 19 | export function minimum(ord: Ord) { 20 | /** 21 | * Gets the minimum value from a {@link AsyncStream}. 22 | * 23 | * @step 1 24 | * @template A The value type. 25 | * @param {AsyncStream} xs The stream instance to find its minimum value. 26 | * @return {TaskOption} The minimum value found in the stream. 27 | * 28 | * @__PURE__ 29 | */ 30 | return function _minimum(xs: AsyncStream): TaskOption { 31 | return async function __minimum() { 32 | let gen = xs() 33 | 34 | let { value: lhs, done } = await gen.next() 35 | if (done) return none 36 | 37 | for await (const rhs of gen) { 38 | if (ord.compare(lhs, rhs) === 1) { 39 | lhs = rhs 40 | } 41 | } 42 | 43 | return some(lhs) 44 | } 45 | } 46 | } 47 | 48 | /** 49 | * Gets the maximum value from a {@link AsyncStream}. 50 | * 51 | * @export 52 | * @template A The value type. 53 | * @param {Ord} ord The {@link Ord} instance of the values. 54 | * @return {(xs: AsyncStream) => TaskOption} A function that takes a stream to 55 | * extract the maximum value. 56 | * 57 | * @__PURE__ 58 | */ 59 | export function maximum(ord: Ord) { 60 | /** 61 | * Gets the maximum value from a {@link AsyncStream}. 62 | * 63 | * @step 1 64 | * @template A The value type. 65 | * @param {AsyncStream} xs The stream instance to find its maximum value. 66 | * @return {TaskOption} The maximum value found in the stream. 67 | * 68 | * @__PURE__ 69 | */ 70 | return function _maximum(xs: AsyncStream): TaskOption { 71 | return async function __maximum() { 72 | let gen = xs() 73 | 74 | let { value: lhs, done } = await gen.next() 75 | if (done) return none 76 | 77 | for await (const rhs of gen) { 78 | if (ord.compare(lhs, rhs) === -1) { 79 | lhs = rhs 80 | } 81 | } 82 | 83 | return some(lhs) 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Stream/utils/zip-array.ts: -------------------------------------------------------------------------------- 1 | import { pipe } from 'fp-ts/lib/function' 2 | 3 | import { map } from '../functor' 4 | import { Stream } from '../uri' 5 | 6 | /** 7 | * Takes two {@link Stream}s and returns a {@link Stream} of corresponding 8 | * pairs as a tuple of both elements. 9 | * 10 | * @export 11 | * @template B The value type. 12 | * @param {Stream} fb The input stream. 13 | * @return {(fa: Stream) => Stream} A function that 14 | * takes another stream to zip. 15 | * 16 | * @__PURE__ 17 | */ 18 | export function zipArray(fb: Stream): (fa: Stream) => Stream 19 | 20 | /** 21 | * Takes two {@link Stream}s and returns a {@link Stream} of corresponding 22 | * pairs as a tuple of both elements. 23 | * 24 | * @export 25 | * @template A The value of the first stream type. 26 | * @template B The value of the second stream type. 27 | * @param {Stream} fa The first stream. 28 | * @param {Stream} fb The second stream. 29 | * @return {Stream} The output stream. 30 | * 31 | * @__PURE__ 32 | */ 33 | export function zipArray(fa: Stream, fb: Stream): Stream 34 | export function zipArray(fAOrB: Stream | Stream, fb?: Stream): Stream | ((fa: Stream) => Stream) { 35 | if (typeof fb !== 'undefined') { 36 | return function* __zipArray() { 37 | const genA = fAOrB() 38 | const genB = fb() 39 | 40 | while (true) { 41 | const currA = genA.next() 42 | const currB = genB.next() 43 | 44 | if (currA.done || currB.done) return 45 | yield [ currA.value, currB.value ] as [ A, B ] 46 | } 47 | } 48 | } 49 | 50 | return function _zipArray(ma: Stream): Stream { 51 | return zipArray(ma, fAOrB as Stream) 52 | } 53 | } 54 | 55 | /** 56 | * Reverse of {@link zipArray}. 57 | * 58 | * Takes a {@link Stream} of pairs and returns a tuple of {@link Stream}s. 59 | * 60 | * @export 61 | * @template A The value type of the first stream. 62 | * @template B The value type of the second stream. 63 | * @param {(Stream)} mma The input stream of streams. 64 | * @return {[ Stream, Stream ]} A tuple of elements from both streams. 65 | * 66 | * @__PURE__ 67 | */ 68 | export function unzipArray(mma: Stream): [ Stream, Stream ] { 69 | return [ 70 | pipe(mma, map(it => it[ 0 ])), 71 | pipe(mma, map(it => it[ 1 ])) 72 | ] 73 | } 74 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/intersection.ts: -------------------------------------------------------------------------------- 1 | import { Eq } from 'fp-ts/lib/Eq' 2 | 3 | import { toArray } from '../conversions' 4 | import { AsyncStream } from '../uri' 5 | 6 | /** 7 | * Creates an {@link AsyncStream} of unique values that included in all given 8 | * {@link AsyncStream} using a {@link Eq} for equality comparisons. 9 | * 10 | * The order and references of result values are determined by the first 11 | * {@link AsyncStream}. 12 | * 13 | * @export 14 | * @template A The value type. 15 | * @param {Eq} E The equality comparer instance. 16 | * @return {(xs: AsyncStream) => (ys: AsyncStream) => AsyncStream} A 17 | * function that takes an async stream to modify. 18 | * 19 | * @__PURE__ 20 | */ 21 | export function intersection(E: Eq) { 22 | /** 23 | * Takes an {@link AsyncStream} to get the intersection between that and the 24 | * other. 25 | * 26 | * @param {AsyncStream} xs The first async stream. 27 | * @return {(ys: AsyncStream) => AsyncStream} A function that takes the 28 | * other async stream. 29 | * 30 | * @step 1 31 | * @__PURE__ 32 | */ 33 | function _intersection(xs: AsyncStream): (ys: AsyncStream) => AsyncStream 34 | 35 | /** 36 | * Takes an {@link AsyncStream} to get the intersection between that and the 37 | * other. 38 | * 39 | * @param {AsyncStream} xs The first async stream. 40 | * @param {AsyncStream} ys The second async stream. 41 | * @return {AsyncStream} An async stream containing the intersections of 42 | * the given streams. 43 | * 44 | * @step 1 45 | * @__PURE__ 46 | */ 47 | function _intersection(xs: AsyncStream, ys: AsyncStream): AsyncStream 48 | function _intersection(xs: AsyncStream, ys?: AsyncStream): AsyncStream | ((ys: AsyncStream) => AsyncStream) { 49 | if (typeof ys !== 'undefined') { 50 | return async function* _intersection() { 51 | const genX = xs() 52 | let result: IteratorResult = await genX.next() 53 | if (result.done) return 54 | 55 | const collected = await toArray(ys) 56 | 57 | while (!result.done) { 58 | const value = result.value 59 | 60 | if (collected.some(it => E.equals(it, value))) { 61 | yield value 62 | } 63 | 64 | result = await genX.next() 65 | } 66 | } 67 | } 68 | 69 | return function _intersection_intersection(my: AsyncStream): AsyncStream { 70 | return _intersection(xs, my) 71 | } 72 | } 73 | 74 | return _intersection 75 | } 76 | -------------------------------------------------------------------------------- /src/AsyncStream/utils/drop-left-while.ts: -------------------------------------------------------------------------------- 1 | import { Refinement } from 'fp-ts/lib/Refinement' 2 | 3 | import { AsyncStream } from '../uri' 4 | import { AsyncPredicate } from './async-predicate' 5 | 6 | /** 7 | * Creates a new {@link AsyncStream} which is a copy of the input dropping the 8 | * longest initial substream for which all element satisfy the specified 9 | * predicate/refinement. 10 | * 11 | * @export 12 | * @template A The value type. 13 | * @template B The refined value type. 14 | * @param {Refinement} refinement The refinement/predicate function. 15 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 16 | * async stream to drop its left while the condition holds. 17 | * 18 | * @__PURE__ 19 | */ 20 | export function dropLeftWhile( 21 | refinement: Refinement 22 | ): (fa: AsyncStream) => AsyncStream 23 | 24 | /** 25 | * Creates a new {@link AsyncStream} which is a copy of the input dropping the 26 | * longest initial substream for which all element satisfy the specified 27 | * predicate/refinement. 28 | * 29 | * @export 30 | * @template A The value type. 31 | * @param {AsyncPredicate} predicate The refinement/predicate function. 32 | * @return {(fa: AsyncStream) => AsyncStream} A function that 33 | * takes an stream to drop its left while the condition holds. 34 | * 35 | * @__PURE__ 36 | */ 37 | export function dropLeftWhile( 38 | predicate: AsyncPredicate 39 | ): (fb: AsyncStream) => AsyncStream 40 | 41 | /** 42 | * Creates a new {@link AsyncStream} which is a copy of the input dropping the 43 | * longest initial substream for which all element satisfy the specified 44 | * predicate/refinement. 45 | * 46 | * @export 47 | * @template A The value type. 48 | * @param {AsyncPredicate} predicate The refinement/predicate function. 49 | * @return {(fa: AsyncStream) => AsyncStream} A function that takes an 50 | * async stream to drop its left while the condition holds. 51 | * 52 | * @__PURE__ 53 | */ 54 | export function dropLeftWhile(predicate: AsyncPredicate): (fa: AsyncStream) => AsyncStream 55 | export function dropLeftWhile(predicate: AsyncPredicate): (fa: AsyncStream) => AsyncStream { 56 | return function _dropLeftWhile(fa) { 57 | return async function* __dropLeftWhile() { 58 | const gen = fa() 59 | 60 | for await (const a of gen) { 61 | if (await predicate(a)) { 62 | continue 63 | } 64 | 65 | yield a 66 | yield* gen 67 | return 68 | } 69 | } 70 | } 71 | } 72 | --------------------------------------------------------------------------------