gaurav5430
gaurav5430

Reputation: 13902

Javascript Pipe function - why do all standard implementations return a function which takes an argument

I have seen a lot of implementations for pipe in javascript. All of them are written in a functional programming way, starting with a function which takes all the fns to be piped as an argument and then returning a function which takes the argument to the first function. This returned function is then responsible for piping the fns.

Something like this:

pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x);

Are there any issues with writing this function in a way that it does need to return a function and can just take the parameter in its arguments:

pipe = (x, ...fns) => fns.reduce((v, f) => f(v), x);

Would both of them work the same? Is one better than the other ?

Upvotes: 0

Views: 404

Answers (2)

user5536315
user5536315

Reputation:

Given curry = f => x => y => f(x, y) and uncurry = f => (x, y) => f(x) (y) they are the same, i.e. you can convert one form into the other and vice versa (ignoring the rest parameter).

But the question is why would you burden yourself with function arity in the first place? Why should functions differ in arity at all? If every functions only accept a single argument, their API would be simplified and we could combine them with fewer restrictions:

const fold = f => acc => xs => xs.reduce((acc, x) => f(acc) (x));
const pipe = g => f => x => f(g(x));
const flip = f => y => x => f(x) (y);
const id = x => x;

const pipen = fold(pipe) (id);
const comp = flip(pipe);
const compBin = comp(comp) (comp);
const compBin3 = compBin(compBin) (compBin);

const inc = x => x + 1;
const sqr = x => x * x;
const add = x => y => x + y;

console.log(
  "pipen",
  pipen([inc, inc, inc, inc, sqr]) (1)); // 25

console.log(
  "compBin",
  compBin(add) (add) (2) (3) (4)); // 9
  
console.log(
  "compBin3",
  compBin3(add) (add) (add) (2) (3) (4) (5)); // 14

No one would define comp and compBin the way I did. It should just illustrate the possibilities.

Upvotes: 1

CertainPerformance
CertainPerformance

Reputation: 371019

The first method is more general, and as a result, probably more useful. Consider the situation in which you'd want to call some piped functions on multiple inputs, not just one:

const pipe = (x, ...fns) => fns.reduce((v, f) => f(v), x);
console.log(
  pipe(1, x => x + 2, x => x * 2),
  pipe(3, x => x + 2, x => x * 2),
);

That's ugly. A higher-order function lets you write less repetitive code:

const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);
const changeNum = pipe(x => x + 2, x => x * 2);
console.log(
  changeNum(1),
  changeNum(3),
);

If you're in a situation where you don't have to call the piped function more than once, there's not much concrete benefit of one method over the other (except, perhaps, for argument typing - having arguments which are all the same, such as ...fns, may be considered to be more elegant than mixing arguments which denote fundamentally different things, such as (x, ...fns)).

That said, it's easier not to have to think about which situation you're in, in which case the standard pipe function of (...fns) => x => is preferable anyway, since it'll flexible enough for all situations, unlike the alternative.

Upvotes: 4

Related Questions