FP/curry.js

const length = require('../Utility/length');
const array_rest = require('../Array/array_rest');
const to_array = require('../Convertor/to_array');
const is_function = require('../Assertion/is_function');
const arity = require('./arity');
const throwFunctionTypeError = require('../Errors/functionTypeError');

/**
 * Currying refers to the process of transforming a function with multiple arity
 * into the same function with less arity.
 *
 * The curried effect is achieved by binding some of the arguments to the first function invoke,
 * so that those values are fixed for the next invocation.
 *
 * @memberof module:FP
 * @param {Function} fn The function to curry.
 * @return {Function} Curried function.
 * @since 0.1.0
 * @throws {TypeError}
 * @see {@link https://en.wikipedia.org/wiki/Currying|Currying}
 * @example
 * const sum = (a, b, c) => a + b + c;
 * const sumBy5 = curry(sum, 5);
 * const sumBy8 = curry(sum)(2)(6);
 *
 * sumBy5(3, 5); // => 13
 * sumBy8(6); // => 14
 */
function curry(fn) {
    if (!is_function(fn)) {
        throwFunctionTypeError(fn);
    }

    const numOfArgs = arity(fn);

    return (function takeArguments(initialArguments) {
        return function takeCurriedArguments() {
            var allArguments = initialArguments.concat(to_array(arguments));
            return length(allArguments) >= numOfArgs ? fn.apply(this, allArguments) : takeArguments(allArguments);
        };
    })(array_rest(to_array(arguments)));
}

module.exports = curry;