0

Assuming I have a bunch of functions of arity 2: f: a b -> x, g: c d -> y, etc. up to unary function u: a -> a. What I would like to do is to chain them in such a way:

f(_, g(_, .... z(_, u(_))...)

where inside _ placeholders consecutive values from given input array will be injected. I'm trying to solve this using Ramda library.

Another, very similar problem I have is chaining the functions the same way but the _ placeholder being filled with the same value against which this composition is being executed.

To be more specific:

// 1st problem
someComposition( f(v[0], g(v[1], ..... z(v[n-1], u(v[n]))...) )(v);

// 2nd problem
someComposition2( f(v, g(v, ..... z(v, u(v))...) )(v);

Best what I could came up with for 2nd problem was, assuming all function are currable, following piece of code (hate it because of the (v) repetitions):

compose(
    z(v),
    ...
    g(v),
    f(v),
    u
)(v);

I tried solving it with compose, composeK, pipe, ap but none of them seem to applied to this situation or I just simply am not able to see the solution. Any help is more then welcome.

SOReader
  • 5,697
  • 5
  • 31
  • 53

2 Answers2

1

There's probably some handy Ramda function that I don't know of, or some super functional combinator-thingy I don't understand that makes this easy, but anyway:


You could create your own composition function to compose a list of binary functions and inject values. This function takes a list of functions and a list of arguments. It partially applies the first function it gets to the first argument and keeps on doing so until it's out of arguments, at which it returns a final composed (unary) function:

// Utils
const { add, subtract, multiply, identity, compose, isNil } = R;
const square = x => x * x;

// Our own compose method
const comp2_1 = ([f2, ...f2s ], [x, ...xs], f = identity) =>
  isNil(x)
    ? compose(f2, f)
    : comp2_1(f2s, xs, compose(f2(x), f));


// An example
const myFormula = comp2_1(
  [add, subtract, multiply, square], 
  [10, 5, 2]);

// 3 + 10 = 13
// 13 - 5 = 8
// 8 * 2 = 16
// 16 * 16 = 256
console.log(myFormula(3));
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>

This example will only work for xs.length === fs.length + 1. You might want it to be a bit more flexible by, for instance, continuing the composition even when we're out of arguments:

/* ... */
isNil(x)
  ? isNil(f2) 
    ? f
    : comp2_1(f2s, [], compose(f2, f)) 
  : /* ... */
user3297291
  • 22,592
  • 4
  • 29
  • 45
1

There's nothing directly built into Ramda for either of these. (Disclaimer: I'm one of the Ramda authors.) You can create composition functions like these, if you like, though:

const {tail, compose, reduce, identity, reverse} = R;

const f = (x, y) => `f(${x}, ${y})`;
const g = (x, y) => `g(${x}, ${y})`;
const h = (x, y) => `h(${x}, ${y})`;
const i = (x, y) => `i(${x}, ${y})`;
const j = (x) => `j(${x})`;

const multApply = (fns) => (...vals) => fns.length < 2 
      ? fns[0].apply(null, vals) 
      : fns[0](vals[0], multApply(tail(fns))(...tail(vals)));

console.log(multApply([f, g, h, i, j])('a', 'b', 'c', 'd', 'e'));
                               //=> f(a, g(b, h(c, i(d, j(e)))))

const nest = compose(reduce((g, f) => (v) => f(v, g(v)), identity), reverse);

console.log(nest([f, g, h, i, j])('v')) //=> f(v, g(v, h(v, i(v, j(v)))));
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>

Neither of these does any error checking for empty lists, or for an arguments list shorter than the function list (in the first case.) But other than that, they seem to fit the bill.

There's sure to be a recursive version of the second one to match the first, but this implementation is already fairly simple. I didn't readily see a version of the first as simple as the second, but it might well exist.

Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103