144 lines
3 KiB
JavaScript
144 lines
3 KiB
JavaScript
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<U, V>} f
|
|
* @param {Morphism<T, U>} 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<T>} pred
|
|
* @param {InferredMorphism<T>} pass
|
|
* @param {InferredMorphism<T>} fail
|
|
* @param {T} x
|
|
*/
|
|
(pred, pass, fail, x) => (
|
|
pred(x) ? pass(x) : fail(x)))
|
|
|
|
export const when = curry(
|
|
/**
|
|
* @template T
|
|
* @param {Predicate<T>} pred
|
|
* @param {InferredMorphism<T>} pass
|
|
* @param {T} x
|
|
*/
|
|
(pred, pass, x) => ifElse(pred, pass, id, x))
|
|
|
|
export const unless = curry(
|
|
/**
|
|
* @template T
|
|
* @param {Predicate<T>} pred
|
|
* @param {InferredMorphism<T>} fail
|
|
* @param {T} x
|
|
*/
|
|
(pred, fail, x) => ifElse(pred, id, fail, x))
|
|
|
|
/**
|
|
* @template A, B
|
|
* @template {Morphism<A, B>} [F=Morphism<A, B>]
|
|
* @typedef {(f: F, a: A) => B} StaticMorphism
|
|
*/
|
|
|
|
export const ap = curry(dispatch(['fantasy-land/ap', 'ap'],
|
|
/**
|
|
* @template A, B
|
|
* @template {Morphism<A, B>} Ap
|
|
* @param {Morphism<A, B>} 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) => a.map(f)
|
|
))
|
|
|
|
export const map = curry(dispatch(['fantasy-land/map', 'map'],
|
|
(f, a) => a.map(f)
|
|
))
|
|
|
|
export const reduce = curry(dispatch(['fantasy-land/reduce', 'reduce'],
|
|
(f, acc, xs) => xs.reduce(f, acc)
|
|
))
|
|
|