5
\$\begingroup\$

I want to perform function composition like this:

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

I have already compn:

const id = x => x;

const comp = f => g => x => f(g(x));

const foldr = f => acc => xs =>
 xs.reduceRight((acc, x, i) => 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 => x;

const last = xs => xs[xs.length - 1];

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

const sqr = x => x * x;

const inc = x => x + 1;

const sub = y => x => x - y;

const uncurry = f => (y, x) => f(x) (y);

const comp = f => g => x => f(g(x));

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


// derived functions

const compn = foldr(comp) (id);


// wrapper function

const compn2 = fs => x => 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?

\$\endgroup\$
  • \$\begingroup\$ from what I can tell you're trying something that simply won't work in the current form. Fold only accepts one argument. The only way you could work around that restriction is to provide a foldrn (or foldr2). Unfortunately this makes things start to really get difficult. The easiest way I see is to manually uncurry the "innermost" function and only then start folding, which is exactly what you did here... \$\endgroup\$ – Vogel612 Feb 9 '17 at 12:42
1
\$\begingroup\$

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)
// => 4

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

const uncurry = f => (y, x) => 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 => ([x,...xs]) =>
  x === undefined ? f : papply (f(x)) (xs)

console.log(papply (x => y => y - x) ([2,5])) // 3
// (x => y => y - x) (2) (5)
// (y => y - 2) (5)
// 5 - 2
// => 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]) => x =>
  f === undefined ? x : f(compn (fs) (x))

const inc = x => 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)
// => 6


OK, let's get to it then !

const sqr = x => x * x

const inc = x => x + 1

const sub = y => x => x - y

const compn = ([f,...fs]) => x =>
  f === undefined ? x : f(compn (fs) (x))

const papply = f => ([x,...xs]) =>
  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 => x - 2) ([5])))
// sqr (inc (papply (5 - 2) ([])))
// sqr (inc (papply (3) ([])))
// sqr (inc (3))
// sqr (4)
// => 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 ^_^

\$\endgroup\$
  • \$\begingroup\$ It is helpful to consider the problem under a different angle, thank you! \$\endgroup\$ – user130561 Feb 13 '17 at 7:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy