168 lines
2.8 KiB
JavaScript
168 lines
2.8 KiB
JavaScript
import { id, Identity } from './identity.js'
|
|
import { Algebra, Alternative, Foldable, Monad, Setoid } from './interfaces.js'
|
|
|
|
/** @import { Alt, Apply, Chain, Foldable as FoldableT, Functor, Morphism, Setoid as SetoidT } from './types.js' */
|
|
|
|
const Interfaces = Algebra(Setoid, Alternative, Monad, Foldable)
|
|
|
|
/**
|
|
* @template T
|
|
* @typedef {Some<T> | None<T>} Option
|
|
*/
|
|
|
|
/** @template T */
|
|
export class Some extends Interfaces {
|
|
/** @type {T} */
|
|
#value
|
|
|
|
/** @param {T} value */
|
|
constructor(value) {
|
|
super()
|
|
|
|
this.#value = value
|
|
}
|
|
|
|
/**
|
|
* @type {SetoidT<T>['equals']}
|
|
* @param {Some<T>} other
|
|
*/
|
|
equals(other) {
|
|
if (other instanceof Some) { return false }
|
|
const eq = /** @type {Some<T>} */ (other).chain(v => id(v === this.#value))
|
|
return /** @type {Identity<boolean>} */ (eq).extract()
|
|
}
|
|
|
|
/**
|
|
* @template U
|
|
* @type {Apply<T>['ap']}
|
|
* @param {Some<Morphism<T, U>>} b
|
|
* @returns {Some<U>}
|
|
*/
|
|
ap(b) {
|
|
return /** @type {Some<U>} */ (b.chain(f =>
|
|
/** @type {Some<U>} */(this.map(f))
|
|
))
|
|
}
|
|
|
|
/**
|
|
* @type {Alt<T>['alt']}
|
|
*/
|
|
alt(b) {
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* @type {Chain<T>['chain']}
|
|
*/
|
|
chain(f) {
|
|
return f(this.#value)
|
|
}
|
|
|
|
/**
|
|
* @template U
|
|
* @type {Functor<T>['map']}
|
|
* @param {Morphism<T, U>} f
|
|
* @returns {Some<U>}
|
|
*/
|
|
map(f) {
|
|
return /** @type {Some<U>} */ (this.chain(v => some(f(v))))
|
|
}
|
|
|
|
/**
|
|
* @type {Functor<T>['map']}
|
|
*/
|
|
then(f) {
|
|
return this.map(f)
|
|
}
|
|
|
|
/**
|
|
* @template U
|
|
* @type {FoldableT<T>['reduce']}
|
|
* @param {(acc: U, value: T) => U} f
|
|
* @param {U} init
|
|
* @returns {U}
|
|
*/
|
|
reduce(f, init) {
|
|
return f(init, this.#value)
|
|
}
|
|
}
|
|
|
|
/** @template T */
|
|
export class None extends Interfaces {
|
|
/**
|
|
* @type {SetoidT<T>['equals']}
|
|
*/
|
|
equals(other) {
|
|
return other === none
|
|
}
|
|
|
|
/**
|
|
* @type {Apply<T>['ap']}
|
|
* @returns {this}
|
|
*/
|
|
ap(_b) {
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* @type {Alt<T>['alt']}
|
|
*/
|
|
alt(b) {
|
|
return b
|
|
}
|
|
|
|
/**
|
|
* @template U
|
|
* @type {Chain<T>['chain']}
|
|
* @param {Morphism<T, U>} _f
|
|
* @returns {this}
|
|
*/
|
|
chain(_f) {
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* @template U
|
|
* @type {Functor<T>['map']}
|
|
* @param {Morphism<T, U>} _f
|
|
* @returns {this}
|
|
*/
|
|
map(_f) {
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* @template R
|
|
* @type {Functor<T>['map']}
|
|
* @param {Morphism<T, R>} _f
|
|
* @returns {None}
|
|
*/
|
|
then(_f) {
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* @template U
|
|
* @type {FoldableT<T>['reduce']}
|
|
* @param {(acc: U, value: T) => U} _f
|
|
* @param {U} init
|
|
* @returns {U}
|
|
*/
|
|
reduce(_f, init) {
|
|
return init
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @template T
|
|
* @param {T} value
|
|
*/
|
|
export const some = value => new Some(value)
|
|
export const none = new None()
|
|
|
|
const TypeRef = some
|
|
TypeRef.constructor['of'] = some
|
|
TypeRef.constructor['zero'] = none
|
|
|
|
export const Option = TypeRef
|
|
|