import __ from "./placeholderArgument";
import arity from "./arity";
const _curryN = (length, received, func) => (...args) => {
let combined = [];
let argsIdx = 0;
let left = length;
let combinedIdx = 0;
while (combinedIdx < received.length || argsIdx < args.length) {
let arg;
if (
combinedIdx < received.length &&
(received[combinedIdx] !== __ || argsIdx >= args.length)
) {
arg = received[combinedIdx];
} else {
arg = args[argsIdx++];
}
if (arg !== __) {
left--;
}
combined[combinedIdx++] = arg;
}
if (left <= 0) {
return func.apply(this, combined);
}
return arity(left, _curryN(length, combined, func));
};
/**
* Curries the provided function.
* A curried function does not need to have all of its arguments provided at once.
* If `f` is a ternary and `g = curry(f)`, then the following is equivalent:
*
* <pre>
* g(1)(2)(3);
* g(1)(2, 3);
* g(1, 2)(3);
* g(1, 2, 3);
* </pre>
*
* Additionaly, the F.__ symbol can be used as a placeholder argument,
* when you want to partially apply arguments with gaps inbetween them.
*
* <pre>
* g(1, __, 3)(2);
* g(__, __, 3)(1, 2);
* g(__, __, 3)(__, 2)(1);
* </pre>
*
* @param {function} func
* @returns {function}
*/
const curry = (func) => _curryN(func.length, [], func);
export default curry;