kojima/src/algebra/option.js
2025-03-31 05:22:59 -05:00

122 lines
2 KiB
JavaScript

import { chain, constant, Self, Value } from './common.js'
/** @import { Applicative, Morphism, Set } from './common.js' */
/**
* @template T
* @typedef {Some<T> | None} Option
*/
/**
* @template T
* @typedef SomeMethods
* @property {typeof isSome} isSome
* @property {typeof isNone} isNone
* @property {typeof chain} chain
* @property {typeof map} map
* @property {typeof alt} alt
* @property {typeof fold} fold
*/
/**
* @template T
* @typedef {Applicative<T> & Set<T> & SomeMethods<T>} Some
* @variation 1
*/
/**
* @typedef None
* @property {typeof isSome} isSome
* @property {typeof isNone} isNone
* @property {typeof chain} chain
* @property {typeof map} map
* @property {typeof alt} alt
* @property {typeof fold} fold
*/
/**
* @template T, U
* @this {Option<T>}
* @param {Morphism<T, U>} fn
* @returns {Option<U>}
*/
function map(fn) {
return this[Self](this.chain(fn))
}
/**
* @template T, U
* @this {Option<T>}
* @param {Morphism<T, U>} fn
* @param {U} acc
* @return {U}
*/
function fold(fn, acc) {
const result = this.map(fn)
return result.isSome() ? result[Value] : acc
}
/**
* @template T
* @this {Option<T>}
* @param {Option<T>} other
* @returns {Option<T>}
*/
function alt(other) {
return this.isSome() ? this : other
}
/**
* @template T
* @this {Option<T>}
* @returns {this is Some<T>}
*/
function isSome() {
return this[Self] === Some
}
/**
* @template T
* @this {Option<T>}
* @returns {this is None}
*/
function isNone() {
return this === None
}
/**
* @template T
* @param {?T} value
* @returns {Some<T>}
*/
export const Some = value => ({
[Self]: Some,
[Value]: value,
isSome,
isNone,
chain,
map,
alt,
fold
})
/**
* @returns {None}
*/
export const None = () => None
None.isSome = isSome
None.isNone = isNone
None.chain = constant
None.map = constant
None.alt = alt
None.fold = fold
/**
* @template T
* @param {?T} value
* @returns {Option<T>}
*/
export const Option = value => Some(value)
Option.of = Option
Option.zero = None