Reputation: 3644
I am trying to write a simple compose
function that takes a series of functions, and composes them like so:
compose(func1, func2, func3)(n) === func1(func2(func3(n)))
I do so by recursing through a rest parameter,
var compose = function(...args) {
return (n) => {
if (args.length) {
return args[0](compose(args.slice(1)));
} else {
return n;
}
};
};
I then attempt to compose a new function made up of a series of other functions,
var plusOneAndTwo = compose((n) => n + 1, (n) => n + 2);
plusOneAndTwo(1);
Instead of returning 4, I get back the body of the inner, anonymous function inside of compose
as a string,
"(n) => {
if (args.length) {
return args[0](compose(args.slice(1)));
} else {
return n;
}
}1"
Note the "1"
at the end of the string! I have no idea why this is happening, and in particular I'm perplexed by how a 1 is getting appended to the end of that.
Any clarification is appreciated, thanks!
Upvotes: 1
Views: 213
Reputation: 386560
You could take a different approach by returning a new function or returning the last function for calling with arguments.
const
compose = (...fns) => fns.length
? v => compose(...fns.slice(0, -1))(fns.pop()(v))
: v => v,
fn1 = n => n * 5,
fn2 = n => n + 2,
fn3 = n => n * 7;
console.log(fn1(fn2(fn3(1))));
console.log(compose(fn1, fn2, fn3)(1));
Upvotes: 1
Reputation: 138257
You just have to call the composed function:
return args[0](compose(...args.slice(1))(n));
Or without recursion it'll be:
const compose = (...fns) => start => fns.reduceRight((acc, fn) => fn(acc), start);
Upvotes: 2
Reputation: 156
The problem occurs in the recursive call to compose.
In particular, you are not passing the parameter n
to it (as also suggested by others above). Furthermore, you need to expand the rest parameter in the call.
You should use something like:
return args[0](compose(...args.slice(1))(n))
In your case, you are simply returning:
return args[0](compose(args.slice(1)));
In your example you call compose((n) => n + 1, (n) => n + 2);
. Compose then returns a function taking n
as a parameter. In this function, args.length becomes 1 (i.e. true-is), args[0] becomes (n) => n + 1
and args.slice(1) becomes [(n) => n + 2]
.
Next, you call this returned function with the parameter n = 1
. As args.length is 1, the if() statement will go into the if() case. In this if case, it will call args[0] with the argument compose(args.slice(1))
.
In this recursive call, compose(args.slice(1)) is evaluated to a function, taking n as a parameter and the same function body.
This function is then given as the parameter n
to args[0] (in the outer call). Recall that args[0] in this scenario is the function (n) => n + 1
.
Thus the call as a whole is equivalent to:
// returned from the recursive call to compose(args.slice(1))
var n = (n) => {
if (args.length) {
return args[0](compose(args.slice(1)));
} else {
return n;
}
}
// applied to arg[0] == (n) => n + 1
return n + 1
This means that the code will attempt to add a function with the number 1. In JavaScript adding a function and a number results in both objects coerced into a string. Casting a number into a string is trivial, casting a function into a string returns the function source code. These strings are then added to give the return value you saw: The function body as a string with the 1 at the end.
Upvotes: 3