@dovca/fp
Version:
Minimal functional programming in JavaScript
305 lines (266 loc) • 7.52 kB
JavaScript
const debug = require(`${__dirname}/../util/debug`);
/**
* @typedef {*} thing
*/
/**
* @typedef numeric
* @type {number|boolean|null}
*/
/**
* @typedef caseParameter
* @type {array}
* @property {function(): boolean} 0 expression function
* @property {function(): *} 1 callback function - case body
*/
/**
* @typedef keyValuePair
* @type {array}
* @property 0 key
* @property 1 value
*/
/**
* _ - Identity
* @param {*} X anything
* @returns {*} the same thing
*/
_ = (X) => X;
/**
* y - Create array
* @param {...*} V values to put into the array
* @returns {*[]}
*/
y = (...V) => V;
/**
* Cached empty array
* @type {array}
*/
Y = y();
/**
* x - Extract
* @param {number|string} P property name
* @param {object|array|string} S object to get property from
* @returns {*} property A of object: obj[A]
*/
x = (P, {[P]: V}) => V;
/**
* q - Dequeue
* @param {thing[]|string} A array or string
* @returns {thing[]|string[]} array filled with elements of A except the first one
*/
q = ([, ...V]) => V;
/**
* e - Compare equal
* @param {*} [A] first value
* @param {*} [B] second value
* @returns {boolean} true if A is strictly equal to B, false otherwise
*/
e = (A, B) => A === B; //operator ===
/**
* Cached value of true
* @type {boolean}
*/
E = e();
/**
* a - Add
* @param {numeric} [A=false] value to add to
* @param {numeric} [B=true] value to add
* @returns {number} A + B
*/
a = (A = L, B = E) => A + B; //operator +
/**
* l - Compare less
* @param {numeric} [A] first value
* @param {numeric} [B=[]] second value
* @returns {boolean} true if A is less than or equal to B, false otherwise
*/
l = (A, B = Y) => A < B; //operator <
/**
* Cached value of false
* @type {boolean}
*/
L = l();
/**
* b - Subtract
* @param {numeric} [A=false] value to subtract from
* @param {numeric} [B=true] value to subtract
* @returns {number} A - B
*/
b = (A = L, B = E) => a(A, a(~B, E)); //operator ~
/**
* $ - Wrap
* @param {(function(*=): *)|thing} [F] function to use or single value to wrap in a function
* @param {*} [A] first parameter to F
* @param {...*} [B] other parameters to F
* @returns {*|(function(): thing)} returns F if second argument is undefined, otherwise applies A as arguments to function F
*/
$ = (F, A, ...B) => () => i(() => e(A), () => F, () => F(A, ...B));
/**
* z - Zero
* @param {numeric} [A=false] value to convert to number
* @returns {number} A converted to number
*/
z = (A = L) => a(A, L);
/**
* Cached value of 0
* @type {number}
*/
Z = z();
/**
* d - Double
* @param {numeric} [A=true] number to double
* @returns {number} 2 * A
*/
d = (A = E) => a(A, A);
/**
* n - Logical negation
* @param {boolean} [A]
* @returns {boolean} negation of A
*/
n = (A) => e(z(A), Z);
/**
* o - Logical OR
* @param {boolean} [A] first value
* @param {boolean} [B=A] second value
* @returns {boolean} A converted to boolean if B is omitted, A OR B otherwise
*/
o = (A, B = A) => l(L, a(n(n(A)), n(n(B))));
/**
* i - If/else
* @param {function(): boolean} C function that returns the condition expression
* @param {function(): *} T function to call if C() is true
* @param {function(): *} [F=function: undefined] function to call if C() is false
* @returns {*} whatever T() or F() returns
*/
i = (C, T, F = _) => x(z(o(C())), y(F, T))();
/**
* w - While
* @param {function(*): boolean} C function that returns the condition expression
* @param {function(*): *} F function to call each iteration
* @param {*} [R] expression to return at the end
* @returns {*} whatever the last call to F returned or undefined
*/
w = (C, F, R = Z) => i($(C, R), () => w(C, F, F(R)), $(R));
/**
* c - Count
* @param {*} A first element
* @param {...*} [B] other elements
* @param {numeric} [I=0] starting index
* @returns {number} length of array A
*/
c = ([A, ...B], I = Z) => i($(e, A), $(I), $(c, B, a(I)));
/**
* k - If/else not empty
* @param {*[]|string} A array or string
* @param {function(): *} T function to execute if A is not empty
* @param {function(): *} [F=function(): undefined] function to execute if A is empty
* @returns {*} whatever T() or F() returns
*/
k = (A, T, F = _) => i($(e, x(Z, A)), F, T);
/**
* t - Concatenate
* @param {*[]} A array to push into
* @param {...*} [B] values to push
* @returns {*[]} new modified array
*/
t = (A, ...B) => y(...A, ...k(B, $(B), $(Y)));
/**
* g - Compose
* @param {function(*=): *} F first function
* @param {...function(*=): *} [R] other functions
* @returns {function(*=): *} composed function: g(a, b, c)(x) === a(b(c(x)))
*/
g = (F, ...R) => (X) => F(k(R, () => g(...R)(X), $(X)));
/**
* v - Reverse
* @param {thing} [A] first parameter
* @param {...thing} [B] other parameters
* @returns {thing[]} reversed parameters in an array
*/
v = (A, ...B) => i($(e, A), $(Y), () => y(...v(...B), A));
/**
* m - Map
* @param {function(*=): *} F transformer function
* @param {*} A first element
* @param {...*} [B] other elements
* @returns {*[]} elements in an array, each passed through F
*/
m = (F, A, ...B) => y(F(A), ...k(B, $(m, F, ...B), $(B)));
/**
* p - Pipe
* @param {...function(*=): *} F functions
* @returns {function(*=): *}
*/
p = (...F) => g(...v(...F));
/**
* r - Reduce
* @param {function(*,*): *} [F] function(value, accumulator)
* @param {*} [A] first element
* @param {...*} [B] other elements
* @returns {*} the reduced value
*/
r = (F, A, ...B) => k(B, $(F, A, k(B, $(r, F, ...B))), $(A));
/**
* s - String
* @param {...*} [A] values to concatenate in a string
* @returns {string} concatenated values
*/
s = (...A) => r(a, ...A, Y, Y);
/**
* j - Create object
* @param {...keyValuePair} [P] key-value pairs to add to the returned object
* @returns {object}
*/
j = (...P) => k(P, $(r, ([K, V], S) => _({...S, [K]: V}), ...P, j), $({}));
/**
* u - Curry
* @param {function(*): *} F curried function
* @param {numeric} [N=2] depth of currying, arity of the function F
* @param {...*} [A] (internal use) saved argument values
* @returns {function(*=): *}
*/
u = (F, N = d(), ...A) => (X) => i($(l, N, d()), $(F, ...A, X), $(u, F, b(N), ...A, X));
/**
* h - Switch
* @param {caseParameter} C first case
* @param {...caseParameter} [R] other cases
* @returns {*} anything that the executed case returns or undefined if no case was executed
*/
h = (C, ...R) => i(...C, $(k, R, $(h, ...R)));
/**
* f - Filter
* @param {function(*): boolean} F function that returns true for each value that should be picked
* @param {...*} A elements
* @returns {*[]} array with values from A that were picked by F
*/
f = (F, ...A) => r((V, S) => i($(F, V), $(y, V, ...S), $(S)), ...A, Y);
$ = debug.wrap($, '$');
_ = debug.wrap(_, '_');
a = debug.wrap(a, 'a');
b = debug.wrap(b, 'b');
c = debug.wrap(c, 'c');
d = debug.wrap(d, 'd');
e = debug.wrap(e, 'e');
f = debug.wrap(f, 'f');
g = debug.wrap(g, 'g');
h = debug.wrap(h, 'h');
i = debug.wrap(i, 'i');
j = debug.wrap(j, 'j');
k = debug.wrap(k, 'k');
l = debug.wrap(l, 'l');
m = debug.wrap(m, 'm');
n = debug.wrap(n, 'n');
o = debug.wrap(o, 'o');
p = debug.wrap(p, 'p');
q = debug.wrap(q, 'q');
r = debug.wrap(r, 'r');
s = debug.wrap(s, 's');
t = debug.wrap(t, 't');
u = debug.wrap(u, 'u');
v = debug.wrap(v, 'v');
w = debug.wrap(w, 'w');
x = debug.wrap(x, 'x');
y = debug.wrap(y, 'y');
z = debug.wrap(z, 'z');
module.exports = {
$, _, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, ...debug
};