175 lines
2.8 KiB
JavaScript
175 lines
2.8 KiB
JavaScript
import { Algebra, Monad } from './interfaces.js'
|
|
|
|
/** @import { Applicative, ApplicativeTypeRef, Apply, Chain, Functor, Morphism } from './types.js' */
|
|
|
|
const Interfaces = Algebra(Monad)
|
|
|
|
/**
|
|
* @template T
|
|
* @typedef {Applicative<T> & (Pure<T> | Impure<T>)} Free
|
|
*/
|
|
|
|
/**
|
|
* @template T
|
|
* @implements {Applicative<T>}
|
|
*/
|
|
class Pure extends Interfaces {
|
|
#value
|
|
|
|
/**
|
|
* @param {T} value
|
|
*/
|
|
constructor(value) {
|
|
super()
|
|
this.#value = value
|
|
}
|
|
|
|
/**
|
|
* @template T
|
|
* @param {T} value
|
|
*/
|
|
static of(value) {
|
|
return liftF(value)
|
|
}
|
|
|
|
/**
|
|
* @template U
|
|
* @type {Chain<T>['chain']}
|
|
* @param {Morphism<T, Pure<U>>} f
|
|
* @returns {Pure<U>}
|
|
*/
|
|
chain(f) {
|
|
return f(this.#value)
|
|
}
|
|
|
|
/**
|
|
* @type {Functor<T>['map']}
|
|
*/
|
|
map(f) {
|
|
return this.chain(x => pure(f(x)))
|
|
}
|
|
|
|
|
|
/**
|
|
* @template U
|
|
* @type {Apply<T>['ap']}
|
|
* @param {Free<Morphism<T, U>>} b
|
|
* @returns {Free<U>}
|
|
*/
|
|
ap(b) {
|
|
return /** @type {Free<U>} */ (b.chain(f =>
|
|
/** @type {Chain<U>} */(this.map(f))
|
|
))
|
|
}
|
|
|
|
interpret() {
|
|
return this.#value
|
|
}
|
|
|
|
toString() {
|
|
return `Pure(${this.#value})`
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @template T
|
|
* @implements {Applicative<T>}
|
|
*/
|
|
class Impure extends Interfaces {
|
|
#value
|
|
#next
|
|
|
|
/**
|
|
* @param {T} value
|
|
* @param {(value: T) => Free<any>} next
|
|
*/
|
|
constructor(value, next) {
|
|
super()
|
|
this.#value = value
|
|
this.#next = next
|
|
}
|
|
|
|
/**
|
|
* @template T
|
|
* @param {T} value
|
|
*/
|
|
static of(value) {
|
|
return liftF(value)
|
|
}
|
|
|
|
/**
|
|
* @template U
|
|
* @type {Chain<T>['chain']}
|
|
* @param {Morphism<T, Free<U>>} f
|
|
* @returns {Free<T>}
|
|
*/
|
|
chain(f) {
|
|
return /** @type {Free<T>} */ (impure(
|
|
this.#value,
|
|
x => /** @type {Free<U>} */(this.#next(x).chain(f))
|
|
))
|
|
}
|
|
|
|
/**
|
|
* @template U
|
|
* @type {Functor<T>['map']}
|
|
* @param {Morphism<T, U>} f
|
|
* @returns {Free<T>}
|
|
*/
|
|
map(f) {
|
|
return /** @type {Free<T>} */ (impure(
|
|
this.#value,
|
|
x => /** @type Free<U>} */(this.#next(x).map(f))
|
|
))
|
|
}
|
|
|
|
/**
|
|
* @template U
|
|
* @type {Apply<T>['ap']}
|
|
* @param {Free<Morphism<T, U>>} b
|
|
* @returns {Free<T>}
|
|
*/
|
|
ap(b) {
|
|
return /** @type {Free<T>} */ (impure(
|
|
this.#value,
|
|
x => /** @type {Free<U>} */(b.chain(f =>
|
|
/** @type {Free<U>} */(this.#next(x).map(f)))
|
|
)
|
|
))
|
|
}
|
|
|
|
interpret() {
|
|
return this.#next(this.#value).interpret()
|
|
}
|
|
|
|
toString() {
|
|
return `Impure(${this.#value}, ${this.#next})`
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @template T
|
|
* @param {T} value
|
|
*/
|
|
export const pure = value => new Pure(value)
|
|
|
|
/**
|
|
* @template T
|
|
* @param {T} x
|
|
* @param {(value: T) => Free<any>} f
|
|
*/
|
|
export const impure = (x, f) => new Impure(x, f)
|
|
|
|
/**
|
|
* @template T
|
|
* @param {T} value
|
|
*/
|
|
export const liftF = value => impure(value, pure)
|
|
|
|
/**
|
|
* @template T
|
|
* @type {ApplicativeTypeRef<T, Free<T>>}
|
|
*/
|
|
export const Free = Pure
|
|
|