287 lines
5 KiB
JavaScript
287 lines
5 KiB
JavaScript
import { id, Identity } from './identity.js'
|
|
import { Algebra, Alternative, Bifunctor, Foldable, Monad, Setoid } from './interfaces.js'
|
|
|
|
/** @import { Alt, Apply, Chain, Functor, Bifunctor as BifunctorT, Foldable as FoldableT, Morphism, Setoid as SetoidT } from './types.js' */
|
|
|
|
const Interfaces = Algebra(Setoid, Alternative, Monad, Foldable, Bifunctor)
|
|
|
|
/**
|
|
* @template T, E
|
|
* @typedef {Ok<T, E> | Err<T, E>} Result
|
|
*/
|
|
|
|
/**
|
|
* @template T, E
|
|
*/
|
|
export class Ok extends Interfaces {
|
|
/** @type {T} */
|
|
#value
|
|
|
|
/**
|
|
* @param {T} value
|
|
* @constructs {Ok<T, E>}
|
|
*/
|
|
constructor(value) {
|
|
super()
|
|
|
|
this.#value = value
|
|
}
|
|
|
|
/**
|
|
* @type {SetoidT<T>['equals']}
|
|
* @param {Result<T, E>} other
|
|
* @returns {boolean}
|
|
*/
|
|
equals(other) {
|
|
if (!(other instanceof Ok)) { return false }
|
|
const eq = other.chain(v => (id(v === this.#value)))
|
|
return /** @type {Identity<boolean>} */ (eq).extract()
|
|
}
|
|
|
|
/** @returns {this is Ok<T, E>} */
|
|
isOk() { return true }
|
|
|
|
/** @returns {this is Err<T, E>} */
|
|
isErr() { return false }
|
|
|
|
/**
|
|
* @type {Chain<T>['chain']}
|
|
*/
|
|
chain(f) {
|
|
return f(this.#value)
|
|
}
|
|
|
|
/**
|
|
* @template E2
|
|
* @type {Chain<E>['chain']}
|
|
* @param {Morphism<E, E2>} _f
|
|
* @returns {this}
|
|
*/
|
|
chainErr(_f) {
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* @template U
|
|
* @type {Functor<T>['map']}
|
|
* @param {Morphism<T, U>} f
|
|
* @returns {Functor<U>}
|
|
*/
|
|
map(f) {
|
|
return this.chain(v => ok(f(v)))
|
|
}
|
|
|
|
/**
|
|
* @template E2
|
|
* @type {Functor<E>['map']}
|
|
* @this {Result<T, E>}
|
|
* @param {Morphism<E, E2>} _f
|
|
* @returns {Result<T, E2>}
|
|
*/
|
|
mapErr(_f) {
|
|
return /** @type {never} */ (this)
|
|
}
|
|
|
|
/**
|
|
* @template U
|
|
* @type {Apply<T>['ap']}
|
|
* @param {Apply<Morphism<T, U>>} b
|
|
* @returns {Result<U, E>}
|
|
*/
|
|
ap(b) {
|
|
return /** @type {Result<U, E>} */ (this.chain(v =>
|
|
/** @type {Chain<U>} */(b.map(f => f(v)))
|
|
))
|
|
}
|
|
|
|
/**
|
|
* @type Alt<T>['alt']
|
|
*/
|
|
alt(_b) {
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* @template U
|
|
* @borrows {Result~map}
|
|
* @param {Morphism<T, U>} f
|
|
*/
|
|
then(f) {
|
|
return this.map(f)
|
|
}
|
|
|
|
/**
|
|
* @template R
|
|
* @param {Morphism<E, R>} _f
|
|
* @returns {this}
|
|
*/
|
|
catch(_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 f(init, this.#value)
|
|
}
|
|
|
|
/**
|
|
* @template T2, E2
|
|
* @type {BifunctorT<T, E>['bimap']}
|
|
* @param {Morphism<T, T2>} f
|
|
* @param {Morphism<E, E2>} _g
|
|
* @returns {Result<T2, E2>}
|
|
*/
|
|
bimap(f, _g) {
|
|
return /** @type {Result<T2, E2>} */ (this.map(f))
|
|
}
|
|
|
|
toString() {
|
|
return `Ok(${this.#value})`
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @template T, E
|
|
*/
|
|
export class Err extends Interfaces {
|
|
/** @type {E} */
|
|
#value
|
|
|
|
/**
|
|
* @param {E} value
|
|
* @constructs {Err<T, E>}
|
|
*/
|
|
constructor(value) {
|
|
super()
|
|
this.#value = value
|
|
}
|
|
|
|
/**
|
|
* @type {SetoidT<T>['equals']}
|
|
* @param {Err<T, E>} other
|
|
* @returns {boolean}
|
|
*/
|
|
equals(other) {
|
|
if (!(other instanceof Err)) { return false }
|
|
const eq = other.chainErr(v => id(v === this.#value))
|
|
return /** @type {Identity<boolean>} */ (eq).extract()
|
|
}
|
|
|
|
/** @returns {this is Ok<T, E>} */
|
|
isOk() { return false }
|
|
|
|
/** @returns {this is Err<T, E>} */
|
|
isErr() { return true }
|
|
|
|
/**
|
|
* @type {Chain<T>['chain']}
|
|
* @returns {this}
|
|
*/
|
|
chain(_f) {
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* @template E2
|
|
* @type {Chain<E>['chain']}
|
|
* @param {Morphism<E, Result<T, E2>>} f
|
|
* @returns {Result<T, E2>}
|
|
*/
|
|
chainErr(f) {
|
|
return f(this.#value)
|
|
}
|
|
|
|
/**
|
|
* @type {Functor<T>['map']}
|
|
* @returns {this}
|
|
*/
|
|
map(_f) {
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* @template E2
|
|
* @type {Functor<E>['map']}
|
|
* @param {Morphism<E, E2>} f
|
|
* @returns {Result<T, E2>}
|
|
*/
|
|
mapErr(f) {
|
|
return /** @type {Result<T, E2>} */ (this.bimap(id, f))
|
|
}
|
|
|
|
/**
|
|
* @type {Functor<T>['map']}
|
|
* @returns {this}
|
|
*/
|
|
then(_f) {
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* @template R
|
|
* @type {Functor<E>['map']}
|
|
* @param {Morphism<E, R>} f
|
|
* @returns {Err<T, R>}
|
|
*/
|
|
catch(f) {
|
|
return new Err(f(this.#value))
|
|
}
|
|
|
|
/**
|
|
* @type Alt<T>['alt']
|
|
*/
|
|
alt(b) {
|
|
return b
|
|
}
|
|
|
|
/**
|
|
* @template U
|
|
* @type {FoldableT<T>['reduce']}
|
|
* @param {(acc: U, value: T) => U} _f
|
|
* @param {U} init
|
|
* @returns {U}
|
|
*/
|
|
reduce(_f, init) {
|
|
return init
|
|
}
|
|
|
|
/**
|
|
* @template T2, E2
|
|
* @type {BifunctorT<T, E>['bimap']}
|
|
* @param {Morphism<T, T2>} _f
|
|
* @param {Morphism<E, E2>} g
|
|
* @returns {Result<T2, E2>}
|
|
*/
|
|
bimap(_f, g) {
|
|
return /** @type {Result<T2, E2>} */ (err(g(this.#value)))
|
|
}
|
|
|
|
toString() {
|
|
return `Err(${this.#value})`
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @template T, E
|
|
* @param {T} v
|
|
* @returns {Ok<T, E>}
|
|
*/
|
|
export const ok = v => new Ok(v)
|
|
|
|
/**
|
|
* @template T, E
|
|
* @param {E} e
|
|
* @returns {Err<T, E>}
|
|
*/
|
|
export const err = e => new Err(e)
|
|
|
|
export const TypeRef = ok
|
|
TypeRef.constructor['of'] = ok
|
|
TypeRef.constructor['zero'] = err
|
|
|