The problem is that you currently have one single instance of args that is shared between all versions of the decorated curried function. This means that once you acquire enough arguments once any further curried calls will just go directly to return fn(...args) and thus executing again will fail:
console.log(sum (1) (2) (3))
// ^^^ ^^^ ^^^
// | | |
//args = [1] -----------------+ | |
//args = [1, 2] ------------------+ |
//args = [1, 2, 3] -------------------+
console.log(sum (1, 2) (3))
// ^^^^^^
// |
//args = [1, 2, 3, 1, 2] ------+
On the fourth call sum(1, 2) the args array contains [1, 2, 3, 1, 2] which is more than fn.length, therefore sum(1, 2) will execute the function and return 6. The next (3) will attempt to execute the number as a function and fail:
const curryDecorate = (fn, ...args) => {
const curried = (...newArgs) => {
args = args.concat(newArgs)
if (args.length < fn.length) {
return curried
}
return fn(...args)
}
return curried
}
const add = (a, b, c) => a + b + c
const sum = curryDecorate(add)
console.log(sum(1)(2)(3)) // 6
const result = sum(1, 2);
console.log(result) // 6
console.log(result(3)) // error
You can take advantage of the fact that curryDecorate already takes args as a parameter, in order to avoid this error. If a curried call does not fulfil the argument requirements, you can call curryDecorate again with all arguments so far. That way you get separate function objects each with with a separate partially filled argument list:
const curryDecorate = (fn, ...args) => {
const curried = (...newArgs) => {
//collect all into newArgs
newArgs = args.concat(newArgs)
if (newArgs.length < fn.length) {
//call curryDecorate with arguments so far
return curryDecorate(fn, ...newArgs)
}
//execute with all collected args
return fn(...newArgs)
}
return curried
}
const add = (a, b, c) => a + b + c
const sum = curryDecorate(add)
console.log(sum(1)(2)(3)) // 6
console.log(sum(1, 2)(3)) // 6
console.log(sum(1)(2, 3)) // 6
console.log("-----");
const sum1 = sum(1);
const sum14 = sum1(4);
const sum15 = sum1(5);
const sum144 = sum14(4);
const sum145 = sum14(5);
const sum155 = sum15(5);
const sum156 = sum15(6);
console.log(sum144);
console.log(sum145);
console.log(sum155);
console.log(sum156);
.as-console-wrapper { max-height: 100% !important; }
Taking another step forward, if curryDecorate is going to be called with the arguments anyway, the logic to execute the function or not can be moved there and things are simplified a bit:
const curryDecorate = (fn, ...args) => {
if (args.length >= fn.length)
return fn(...args);
return (...newArgs) =>
curryDecorate(fn, ...args, ...newArgs);
}
const curryDecorate = (fn, ...args) => {
if (args.length >= fn.length)
return fn(...args);
return (...newArgs) =>
curryDecorate(fn, ...args, ...newArgs);
}
const add = (a, b, c) => a + b + c
const sum = curryDecorate(add)
console.log(sum(1)(2)(3)) // 6
console.log(sum(1, 2)(3)) // 6
console.log(sum(1)(2, 3)) // 6
console.log("-----");
const sum1 = sum(1);
const sum14 = sum1(4);
const sum15 = sum1(5);
const sum144 = sum14(4);
const sum145 = sum14(5);
const sum155 = sum15(5);
const sum156 = sum15(6);
console.log(sum144);
console.log(sum145);
console.log(sum155);
console.log(sum156);
.as-console-wrapper { max-height: 100% !important; }