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