Compose n functions where the innermost function expects multiple arguments I want to perform function composition like this:

f(g(...(z(x) (y));


I have already compn:

const id = x =&gt; x;

const comp = f =&gt; g =&gt; x =&gt; f(g(x));

const foldr = f =&gt; acc =&gt; xs =&gt;
 xs.reduceRight((acc, x, i) =&gt; f(x) (acc, i), acc);

const compn = foldr(comp) (id);


I tried to derive the desired function from compn, but failed. All I've got is a hideous wrapper function that applies the composition accordingly:



// generic functions

const id = x =&gt; x;

const last = xs =&gt; xs[xs.length - 1];

const init = xs =&gt; xs.slice(0, -1);

const sqr = x =&gt; x * x;

const inc = x =&gt; x + 1;

const sub = y =&gt; x =&gt; x - y;

const uncurry = f =&gt; (y, x) =&gt; f(x) (y);

const comp = f =&gt; g =&gt; x =&gt; f(g(x));

const foldr = f =&gt; acc =&gt; xs =&gt;
 xs.reduceRight((acc, x, i) =&gt; f(x) (acc, i), acc);


// derived functions

const compn = foldr(comp) (id);


// wrapper function

const compn2 = fs =&gt; x =&gt; compn([...init(fs), last(fs) (x)]);


// arbitrary computation

const f = uncurry(compn2([sqr, inc, sub]));


console.log(f(5, 2)); // yields 16




Is there a way to derive compn2 from compn or from other combinators?
First, there's something weird happening here


const f = uncurry(compn2([sqr, inc, sub]));
console.log(f(5, 2)); // yields 16



I would assume that this composition is doing this

sqr (inc (sub (5) (2)))
// sqr (inc (2 - 5))
// sqr (inc (-3))
// sqr (-2)
// =&gt; 4


But upon closer inspect, it's because your uncurry function is unconventionally flipping the arguments


const uncurry = f =&gt; (y, x) =&gt; f(x) (y);



Nevermind that tho, we don't have to agree on it. I'll show you a way to solve your problem without even depending on it.



I'm going to introduce a function papply which procedurally applies a curried function



const papply = f =&gt; ([x,...xs]) =&gt;
  x === undefined ? f : papply (f(x)) (xs)

console.log(papply (x =&gt; y =&gt; y - x) ([2,5])) // 3
// (x =&gt; y =&gt; y - x) (2) (5)
// (y =&gt; y - 2) (5)
// 5 - 2
// =&gt; 3






I'm also going to introduce a dramatically simplified compn – there's nothing wrong with yours, but it will help reduce some of the complexity of the example I provide.



const compn = ([f,...fs]) =&gt; x =&gt;
  f === undefined ? x : f(compn (fs) (x))

const inc = x =&gt; x + 1

const f = compn ([inc, inc, inc])

console.log(f(3))
// compn ([inc, inc, inc]) (3)
// inc (compn ([inc,inc]) (3))
// inc (inc (compn ([inc]) (3)))
// inc (inc (inc (compn ([]) (3))))
// inc (inc (inc (3)))
// inc (inc (4))
// inc (5)
// =&gt; 6






OK, let's get to it then !



const sqr = x =&gt; x * x

const inc = x =&gt; x + 1

const sub = y =&gt; x =&gt; x - y

const compn = ([f,...fs]) =&gt; x =&gt;
  f === undefined ? x : f(compn (fs) (x))

const papply = f =&gt; ([x,...xs]) =&gt;
  x === undefined ? f : papply (f(x)) (xs) 

const f = compn([sqr, inc, papply(sub)])

console.log(f([2, 5]));
// sqr (inc (papply (sub) ([2,5]))
// sqr (inc (papply (sub (2)) ([5])))
// sqr (inc (papply (x =&gt; x - 2) ([5])))
// sqr (inc (papply (5 - 2) ([])))
// sqr (inc (papply (3) ([])))
// sqr (inc (3))
// sqr (4)
// =&gt; 16






Of course it requires that the function is applied a bit differently ...

// this
f([2,5])

// instead of
f(5,2)


I don't think that change will bother you much tho, especially considering every other function of yours (including compn) is manually curried. This stays true to calling all functions with one arg. If multiple values are required, a tuple is provided – or in this case, an Array, as Tuple is absent in JS.

It's an interesting question and I think you had an interesting approach. The complexity ultimately steered me to thinking about a different solution. Just my 2 cents, hope it helped some ^_^