import { concat } from '../iter.js' import { liftF } from './fn.js' import { Pure, Suspend } from './free.js' import { AlgebraWithBase, Category, Comonad, Foldable, Monoid } from './index.js' /** @import { InferredMorphism } from './types.js' */ const Nil = Symbol('Nil') /** * @template T * @extends {Pure} */ class ListPure extends AlgebraWithBase(Pure)(Category, Foldable, Monoid) { static empty() { return List.empty() } static id() { return List.empty() } head() { return this._value } tail() { return Empty } /** * @template U * @param {(acc: U, value: T) => U} f * @param {U} init * @returns {U} */ reduce(f, init) { return f(init, this._value) } } /** * @template T * @extends {Suspend} */ class ListSuspend extends AlgebraWithBase(Suspend)(Foldable, Monoid, Comonad) { /** * @param {Iterator} iter */ constructor(iter) { const next = iter.next() const value = /** @type {T} */ (next.value) const fn = /** @type {InferredMorphism} */ (next.done ? List.empty : () => new ListSuspend(iter)) super(value, fn) } /** * @template {Iterable} T * @this {ListSuspend>} * @param {Iterator} value * @returns {ListSuspend} */ cons(value) { return new ListSuspend(concat(value, this._value)) } head() { return this._value } tail() { return this.step() } /** * @template U * @param {(acc: U, value: T) => U} f * @param {U} init * @returns {U} */ reduce(f, init) { const acc = f(init, this._value) return this.tail().reduce(f, acc) } extract() { return this.reduce(reduceArray, []) } static empty() { return List.empty() } } /** * @template T * @type {ListPure | ListSuspend} List */ export class List { /** * @template {Iterable} T * @param {T} value * @returns {ListSuspend} */ static of(value) { // @ts-ignore return new ListSuspend(liftF(value)) } /** * @template T * @param {Iterable} iterator * @returns {ListSuspend} */ static from(iterator) { return new ListSuspend(Iterator.from(iterator)) } static empty() { return Empty } } const Empty = new ListPure(Nil) /** * @template T * @param {T[]} acc * @param {T} value * @returns {T[]} */ const reduceArray = (acc, value) => acc.concat(value)