198 lines
3.8 KiB
JavaScript
198 lines
3.8 KiB
JavaScript
import { Algebra, Foldable, Monad, Traversable } from './interfaces.js'
|
|
import { curry } from '../..//vendor/izuna/src/index.js'
|
|
import { Reducer } from './utilities.js'
|
|
|
|
/** @import { Applicative, ApplicativeTypeRef, Apply, Chain, Foldable as FoldableT, Functor, Morphism, Traversable as TraversableT } from './types.js' */
|
|
|
|
const kleisli = curry((f, g, x) => f(x).chain(g))
|
|
|
|
/**
|
|
* @template T
|
|
* @typedef {Pure<T> | Impure<T>} Free
|
|
*/
|
|
|
|
/** @template T */
|
|
export class Pure extends Algebra(Monad, Traversable) {
|
|
#value
|
|
|
|
/**
|
|
* @param {T} value
|
|
*/
|
|
constructor(value) {
|
|
super()
|
|
this.#value = value
|
|
}
|
|
|
|
/**
|
|
* @type {Chain<T>['chain']}
|
|
*/
|
|
chain(f) {
|
|
return f(this.#value)
|
|
}
|
|
|
|
/**
|
|
* @type {Functor<T>['map']}
|
|
*/
|
|
map(f) {
|
|
return pure(f(this.#value))
|
|
}
|
|
|
|
/**
|
|
* @template U
|
|
* @type {Apply<T>['ap']}
|
|
* @this {Pure<Morphism<U, T>>}
|
|
* @param {Pure<U>} b
|
|
* @returns {Free<T>}
|
|
*/
|
|
ap(b) {
|
|
return /** @type {Free<T>} */ (b.map(this.#value))
|
|
}
|
|
|
|
/**
|
|
* @type {FoldableT<T>['reduce']}
|
|
*/
|
|
reduce(f, acc) {
|
|
return f(acc, this.#value)
|
|
}
|
|
|
|
/**
|
|
* @template U
|
|
* @template {Applicative<U>} M
|
|
* @template {ApplicativeTypeRef<U, M>} TypeRef
|
|
* @param {TypeRef} _A
|
|
* @param {(value: T) => M} f
|
|
* @returns {Applicative<Free<U>>}
|
|
*/
|
|
traverse(_A, f) {
|
|
return /** @type {Applicative<Free<U>>} */ (f(this.#value).map(pure))
|
|
}
|
|
|
|
run() {
|
|
return this.#value
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @template T, [N=any]
|
|
* @implements Functor<T>
|
|
* @implements Chain<T>
|
|
* @implements TraversableT<T>
|
|
*/
|
|
export class Impure extends Algebra(Monad, Foldable, Traversable) {
|
|
#value
|
|
#next
|
|
|
|
/**
|
|
* @param {T} value
|
|
* @param {(value: T) => Free<N>} f
|
|
*/
|
|
constructor(value, f) {
|
|
super()
|
|
this.#value = value
|
|
this.#next = f
|
|
}
|
|
|
|
/**
|
|
* @type {Chain<T>['chain']}
|
|
* @template U
|
|
* @param {(value: T) => Free<U>} f
|
|
* @returns {Free<T>}
|
|
*/
|
|
chain(f) {
|
|
return impure(this.#value, kleisli(this.#next, f))
|
|
}
|
|
|
|
/**
|
|
* @template {Functor<T>} U
|
|
* @type {Functor<T>['map']}
|
|
* @param {Morphism<T, U>} f
|
|
* @returns {Free<T>}
|
|
*/
|
|
map(f) {
|
|
return impure(
|
|
this.#value,
|
|
y => /** @type {Free<U>} */(f(y).map(f))
|
|
)
|
|
}
|
|
|
|
/**
|
|
* @template U
|
|
* @type {Apply<T>['ap']}
|
|
* @this {Free<Morphism<U, T>>}
|
|
* @param {Free<U>} b
|
|
* @returns {Free<T>}
|
|
*/
|
|
ap(b) {
|
|
return /** @type {Free<T>} */ (this.chain(f =>
|
|
/** @type {Free<T>} */(b.map(f))
|
|
))
|
|
}
|
|
|
|
/**
|
|
* @type {FoldableT<T>['reduce']}
|
|
*/
|
|
reduce(f, acc) {
|
|
const Const = Reducer(f, acc)
|
|
// @ts-ignore
|
|
return this.traverse(Const, x => new Const(x)).value
|
|
}
|
|
|
|
/**
|
|
* @type {TraversableT<T>['traverse']}
|
|
* @template U
|
|
* @template {Applicative<U>} M
|
|
* @template {ApplicativeTypeRef<U, M>} TypeRef
|
|
* @this {Impure<T, N>}
|
|
* @param {TypeRef} A
|
|
* @param {(value: T | N) => M} f
|
|
* @returns {Applicative<Free<U>>}
|
|
*/
|
|
traverse(A, f) {
|
|
const next = this.#next(this.#value)
|
|
|
|
if (next instanceof Pure) {
|
|
return next.traverse(A, f)
|
|
} else {
|
|
const fb = f(this.#value)
|
|
const rest = next.traverse(A, f)
|
|
|
|
return /** @type {Applicative<Free<U>>} */ (rest.ap(
|
|
/** @type {Apply<Morphism<TraversableT<U>, U>>} */(fb.map(b => next => impure(b, () =>
|
|
/** @type {Free<U>} */(next)
|
|
)))
|
|
))
|
|
}
|
|
}
|
|
|
|
run() {
|
|
return this.#next(this.#value).run()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @template T
|
|
* @param {T} x
|
|
* @returns {Pure<T>}
|
|
*/
|
|
const pure = x => new Pure(x)
|
|
|
|
/**
|
|
* @template T
|
|
* @param {T} x
|
|
* @param {(value: T) => Free<any>} f
|
|
* @returns {Impure<T>}
|
|
*/
|
|
const impure = (x, f) => new Impure(x, f)
|
|
|
|
/**
|
|
* @template T
|
|
* @param {T} x
|
|
* @returns Impure<T>
|
|
*/
|
|
export const liftF = x => impure(x, pure)
|
|
|
|
const TypeRef = () => { }
|
|
TypeRef.constructor.of = pure
|
|
|
|
export const Free = TypeRef
|
|
|