admin管理员组

文章数量:1404927

How can I convert this function position into more readable format?

funcA(argumentA, funcB(argumentA, funcC(argumentA, argumentB)))

What I'd like to achieve is something more like this:

pose(funcC, funcB, funcA)(argumentA, argumentB)

I'm using this pose function implementation:

const pose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)))

The problem is I need argumentA in all function calls as a first parameter and every function returns a value to be passed as a second parameter to the next function. I know I could create separate function-returning functions and use them like so:

pose(funcCWithArg(argumentA), funcBWithArg(argumentA), funcAWithArg(argumentA))(argumentB)

but in my actual case there aren't only three of them, but many more and that would require some serious amount of code just to write them down. Is there a simpler way to do that?

EDIT: I can't use any external library. Only vanilla js.

How can I convert this function position into more readable format?

funcA(argumentA, funcB(argumentA, funcC(argumentA, argumentB)))

What I'd like to achieve is something more like this:

pose(funcC, funcB, funcA)(argumentA, argumentB)

I'm using this pose function implementation:

const pose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)))

The problem is I need argumentA in all function calls as a first parameter and every function returns a value to be passed as a second parameter to the next function. I know I could create separate function-returning functions and use them like so:

pose(funcCWithArg(argumentA), funcBWithArg(argumentA), funcAWithArg(argumentA))(argumentB)

but in my actual case there aren't only three of them, but many more and that would require some serious amount of code just to write them down. Is there a simpler way to do that?

EDIT: I can't use any external library. Only vanilla js.

Share Improve this question edited Jun 19, 2017 at 8:24 zorza asked Jun 19, 2017 at 8:15 zorzazorza 2,8943 gold badges30 silver badges43 bronze badges 5
  • 1 Ramdajs has a function that does that ramdajs./docs/#pose – user93 Commented Jun 19, 2017 at 8:20
  • @user93 From the docs you provided: "The rightmost function may have any arity; the remaining functions must be unary.". So that's not solving my problem. I need two parameters in every function. – zorza Commented Jun 19, 2017 at 8:28
  • 1 @zorza pose partially applied functions with a prebound first argument. – zerkms Commented Jun 19, 2017 at 8:30
  • @zerkms That's the solution I want to avoid if possible as I stated in the question. – zorza Commented Jun 19, 2017 at 8:32
  • 1 @zorza First of all, this isn't function position in the original sense anymore. Sure, you could create your own binator to avoid the nested structure. However, this would disguise the control flow and make the code hard to read. The only reason why you would do this is to remain DRY. I doubt that you will need this binator often, though. So either use partially applied functions or write the function calls manually. – user6445533 Commented Jun 19, 2017 at 10:27
Add a ment  | 

3 Answers 3

Reset to default 3

Using vanilla JS,

const pose = (...fns) => (arg1, arg2) => fns.reduce((arg, f) => f(arg1, arg), arg2);

Explanation

pose bees a function returning a function, which loops through the list of functions passed to it, passing the first argument to every function call.

Test

const sum = (a, b) => (a + b);
const mult = (a, b) => (a * b);
pose(sum, mult)(2, 3) === mult(2, sum(2, 3));  // true

It's not hard to write a function like this:

const link = (...fns) => (a, ...args) =>
    fns.slice(1).reduce((val, fn) => fn(a, val), fns[0](a, ...args));

If you wanted it to be robust enough to handle errors gracefully, it would take more. But this should be a start.

You would use it like this:

const funcA = (x, y) => `funcA(${x}, ${y})`;
const funcB = (x, y) => `funcB(${x}, ${y})`;
const funcC = (x, y) => `funcC(${x}, ${y})`;

link(funcA, funcB, funcC)('argumentA', 'argumentB');
//=> "funcC(argumentA, funcB(argumentA, funcA(argumentA, argumentB)))"

You can see this in action on Runkit.

(And obviously you can do a reverse if you want the opposite argument order.)

First create new functions that are partial applications using bind. Then use the pose function you already have:

const funcA = (x, y) => `A(${x}, ${y})`;
const funcB = (x, y) => `B(${x}, ${y})`;
const funcC = (x, y) => `C(${x}, ${y})`;

const pose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));
const partials = (...fns) => (...args) => fns.map((f) => f.bind(this, ...args));

console.log(pose(...partials(funcA, funcB, funcC)("a"))("b"));

// Output:
// A(a, B(a, C(a, b)))

UPDATE

You can also build a single function that poses the partials with the first argument passed and then calls them with the other arguments. (I think this is what you want? I wasn't 100% sure what to do with more than two arguments.)

const partialCompose = (...fns) => (...args) => pose(...partials(...fns)(args[0]))(...args.slice(1));

console.log(partialCompose(funcA, funcB, funcC)("a", "b")); // same output as above

本文标签: functional programmingCompose javascript functions with more than one argumentStack Overflow