kojima/src/algebra/list.js
2025-04-06 18:23:53 -05:00

134 lines
2.3 KiB
JavaScript

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<T>}
*/
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<T>}
*/
class ListSuspend extends AlgebraWithBase(Suspend)(Foldable, Monoid, Comonad) {
/**
* @param {Iterator<T>} iter
*/
constructor(iter) {
const next = iter.next()
const value = /** @type {T} */ (next.value)
const fn = /** @type {InferredMorphism<T>} */ (next.done ? List.empty : () => new ListSuspend(iter))
super(value, fn)
}
/**
* @template {Iterable<T>} T
* @this {ListSuspend<Iterator<T>>}
* @param {Iterator<T>} value
* @returns {ListSuspend<T>}
*/
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<T> | ListSuspend<T>} List
*/
export class List {
/**
* @template {Iterable<T>} T
* @param {T} value
* @returns {ListSuspend<T>}
*/
static of(value) {
// @ts-ignore
return new ListSuspend(liftF(value))
}
/**
* @template T
* @param {Iterable<T>} iterator
* @returns {ListSuspend<T>}
*/
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)