Here's a 'compose' function which I need to improve:
const compose = (fns) => (...args) => fns.reduceRight((args, fn) => [fn(...args)], args)[0];
Here's a practical implementation of one:
const compose = (fns) => (...args) => fns.reduceRight((args, fn) => [fn(...args)], args)[0]; const fn = compose([ (x) => x - 8, (x) => x ** 2, (x, y) => (y > 0 ? x + 3 : x - 3), ]); console.log(fn("3", 1)); // 1081 console.log(fn("3", -1)); // -8
And here's an improvement my mentor came to.
const compose = (fns) => (arg, ...restArgs) => fns.reduceRight((acc, func) => func(acc, ...restArgs), arg);
If we pass arguments list like that func(x, [y]) with first iteration, I still don't understand how do we make function work with unpacked array of [y]?
compose
doescompose = (fns) =>
(arg, ...restArgs) =>
fns.reduceRight((acc, func) => func(acc, ...restArgs), arg);
When you feed compose
with a number of functions, you get back... a function. In your case you give it a name, fn
.
What does this fn
function look like? By simple substitution you can think of it as this:
(arg, ...restArgs) => fns.reduceRight((acc, func) => func(acc, ...restArgs), arg);
where fns === [(x) => x - 8, (x) => x ** 2, (x, y) => (y > 0 ? x + 3 : x - 3)]
.
So you can feed this function fn
with some arguments, that will be "pattern-matched" against (arg, ...restArgs)
; in your example, when you call fn("3", 1)
, arg
is "3"
and restArgs
is [1]
(so ...restArgs
expands to just 1
after the comma, so you see that fn("3", 1)
reduces to
fns.reduceRight((acc, func) => func(acc, 1), "3");
From this you see that
(x, y) => (y > 0 ? x + 3 : x - 3)
is called with the two arguments "3"
(the initial value of acc
) and 1
,func
, but the point is that the second argument to func
, namely 1
, is only used by the rightmost function, whereas it is passed to but ignored by the other two functions!
Function composition is a thing between unary functions. Using it with functions with higher-than-1 arity leads to confusion.
For instance consider these two functions
square = (x) => x**2; // unary
plus = (x,y) => x + y; // binary
can you compose them? Well, you can compose them into a function like this
sum_and_square = (x,y) => square(plus(x,y));
the compose
function that you've got at the bottom of your question would go well:
sum_and_square = compose([square, plus]);
But what if your two functions were these?
apply_twice = (f) => ((x) => f(f(x))); // still unary, technically
plus = (x,y) => x + y; // still binary
Your compose
would not work.
Even though, if the function plus
was curried, eg if it was defined as
plus = (x) => (y) => x + y
then one could think of composing them in a function that acts like this:
f = (x,y) => apply_twice(plus(x))(y)
which would predictably produce result(3,4) === 10
.
You can get it as f = compose([apply_twice, plus])
.
Additionally, I would suggest a "cosmetic" change: make compose
accept ...fns
instead of fns
,
compose = (...fns)/* I've only added the three dots on this line */ =>
(arg, ...restArgs) =>
fns.reduceRight((acc, func) => func(acc, ...restArgs), arg);
and you'll be able to call it without groupint the functions to be composed in an array, eg you'd write compose(apply_twice, plus)
instead of .compose([apply_twice, plus])
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.