1 | /** @module fp **/
|
2 |
|
3 | /** Reducer function used by `pipe` and `compose` functions to compose sync and async functions
|
4 | * @argument {function|Promise} chain a chain of functions or promises
|
5 | * @argument {function|Promise} func a new function or promise to add to the chain
|
6 | * @method
|
7 | */
|
8 | const mixCompose = (chain, func) => (chain instanceof Promise || typeof chain.then === 'function' ? chain.then(func) : func(chain));
|
9 |
|
10 | /** Compose regular functions or promises generating a final function.
|
11 | * - Compose works from left to right.
|
12 | * - If you compose one single promise the final result will be a promise too.
|
13 | * - You can only compose functions with the same arity.
|
14 | * @argument {function|Promise} arguments N number of functions or promises.
|
15 | * @returns {function|Promise} function or Promise that execute the all composed ones.
|
16 | * @example
|
17 | * const sum3 = a => a+3
|
18 | * const mult2 = a => a*2
|
19 | * const sum2Async = a => Promise.resolve(a+2) // Simulate async response
|
20 | *
|
21 | * const sumAndMult = pipe(sum3,mult2);
|
22 | * sumAndMult(1) // -> (1+3)*2 = 8
|
23 | *
|
24 | * const sumAndMultAsync = pipe(sum3,mult2,sum2Async);
|
25 | * await sumAndMultAsync(1) // -> ((1+3)*2)+2 = 10
|
26 | * @method
|
27 | */
|
28 | const pipe = (...fn) => input => fn.reduce(mixCompose, input);
|
29 |
|
30 | /** Compose regular functions or promises generating a final function.
|
31 | * - Compose works from right to left.
|
32 | * - If you compose one single promise the final result will be a promise too.
|
33 | * - You can only compose functions with the same arity.
|
34 | * @argument {function|Promise} arguments N number of functions or promises.
|
35 | * @returns {function|Promise} function or Promise that execute the all composed ones.
|
36 | * @example
|
37 | * const sum3 = a => a+3
|
38 | * const mult2 = a => a*2
|
39 | * const sum2Async = a => Promise.resolve(a+2) // Simulate async response
|
40 | *
|
41 | * const sumAndMult = compose(sum3,mult2);
|
42 | * sumAndMult(1) // -> (1*2)+3 = 5
|
43 | *
|
44 | * const sumAndMultAsync = compose(sum3,mult2,sum2Async);
|
45 | * await sumAndMultAsync(1) // -> ((1+2)*2)+3 = 9
|
46 | * @method
|
47 | */
|
48 | const compose = (...fn) => input => fn.reduceRight(mixCompose, input);
|
49 |
|
50 | /** Currify any function allowing the partial application of its arguments
|
51 | * @argument {function} function function with at least two arguments
|
52 | * @returns {function} curried function.
|
53 | * @example
|
54 | * const sum = curry((a,b) = a+b);
|
55 | * const sum3 = sum(3);
|
56 | * sum3(3) // -> 6
|
57 | * sum(3,3) // -> 6
|
58 | * sum(3)(3) // -> 6
|
59 | * @method
|
60 | */
|
61 | const curry = f => {
|
62 | if (typeof f !== 'function') {
|
63 | throw new Error(`curry requires a function, [${typeof f}] passed`);
|
64 | }
|
65 | return function currify(...arg) {
|
66 | const args = Array.prototype.slice.call(arg);
|
67 | return args.length >= f.length ? f(...args) : currify.bind(null, ...args);
|
68 | };
|
69 | };
|
70 | module.exports = { pipe, compose, curry };
|