import { curry, curryN } from './curry.js' import { dispatch } from './fantasy-land.js' import { concat as iconcat, iter } from './list.js' /** @import { Fn, Morphism, InferredMorphism, Predicate } from './types.js' */ /** * @template T * @param {T} x * @returns {x} */ export const id = x => x /** * @param {...Fn} fns * @returns {(x: any) => any} */ export const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x) export const concat = curry(dispatch(['fantasy-land/concat', 'concat'], (b, a) => iconcat(iter(a), iter(b)) )) export const compose = curry(dispatch(['fantasy-land/compose', 'compose'], /** * @template T, U, V * @param {Morphism} f * @param {Morphism} g * @param {T} x * @returns V */ (f, g, x) => chain(g, chain(f, x)) )) /** * @template T * @param {T | T[]} a * @Returns {T[]} */ export const liftA = a => Array.isArray(a) ? a : [a] /** * @template T, U, R * @param {(x: T, y: U) => R} binary * @param {T} x * @param {U} y * @returns {R} */ export const liftF = curry((binary, x, y) => binary(x, y)) /** * @param {number} a * @param {number} b * @returns {number} */ export const add = (a, b) => a + b /** * @template T * @param {T} x * @returns {() => T} */ export const thunk = x => () => x /** * @template T, U * @template {(a: T, b: U, ...rest: any[]) => any} F * @param {F} fn * @returns {(b: U, a: T, ...rest: any[]) => any} */ export function flip(fn) { return curryN(fn.length, function(a, b) { const args = Array.prototype.slice.call(arguments, 0) args[0] = b args[1] = a return fn.apply(this, args) }) } export const ifElse = curry( /** * @template T * @param {Predicate} pred * @param {InferredMorphism} pass * @param {InferredMorphism} fail * @param {T} x */ (pred, pass, fail, x) => ( pred(x) ? pass(x) : fail(x))) export const when = curry( /** * @template T * @param {Predicate} pred * @param {InferredMorphism} pass * @param {T} x */ (pred, pass, x) => ifElse(pred, pass, id, x)) export const unless = curry( /** * @template T * @param {Predicate} pred * @param {InferredMorphism} fail * @param {T} x */ (pred, fail, x) => ifElse(pred, id, fail, x)) /** * @template A, B * @template {Morphism} [F=Morphism] * @typedef {(f: F, a: A) => B} StaticMorphism */ export const ap = curry(dispatch(['fantasy-land/ap', 'ap'], /** * @template A, B * @template {Morphism} Ap * @param {Ap} f * @param {{ ap: Ap } | Ap} a */ (f, a) => { const fs = liftA(f) const args = liftA(a) const xs = fs.reduce((acc, f) => ( concat(acc, iter(map(f, args))) ), []) return [...xs] })) export const chain = curry(dispatch(['fantasy-land/chain', 'chain'], (f, a) => f(a) )) export const map = curry(dispatch(['fantasy-land/map', 'map'], (f, a) => chain(f, a) )) export const reduce = curry(dispatch(['fantasy-land/reduce', 'reduce'], (f, acc, xs) => xs.reduce(f, acc) )) export const repeat = curry((n, x) => { const results = [] for (let i = n; i > 0; i--) { results.push(x) } return results })