Hacker News new | past | comments | ask | show | jobs | submit login

One great reason is that the composing function can be optimized away much easier with the native operator.



And a native operator can be optimized just about the same amount as a built-in function. So maybe add the function instead of the operator to the language?


One issue with a `pipe` function is that it is an arity of N. That makes it almost impossible to optimize for all the edge cases. In contrast, the pipe operator is always fixed at an arity of 2. Implementing a static Function.pipe in terms of the primitive operator becomes easy enough. Functional languages have played with both the function and the operator, but they keep coming back to the operator because it's more readable.

    //implementation of your pipe in terms of the (potential) pipe operator
    Function.pipe = (fn, ...args) => {
      //let's use a pseudo Duff's device
      switch (args.length) {
        case 0: return fn;
        case 1: return fn |> args[0];
        case 2: return fn |> args[0] |> args[1];
        case 3: return fn |> args[0] |> args[1] |> args[2];
        case 4: return fn |> args[0] |> args[1] |> args[2] |> args[3];
        case 5: return fn |> args[0] |> args[1] |> args[2] |> args[3] |> args[4];
        case 6: return fn |> args[0] |> args[1] |> args[2] |> args[3] |> args[4] |> args[5];
        case 7: return fn |> args[0] |> args[1] |> args[2] |> args[3] |> args[4] |> args[5] |> args[6];
        case 8: return fn |> args[0] |> args[1] |> args[2] |> args[3] |> args[4] |> args[5] |> args[6] |> args[7];
        default:
          var mod = args.length >> 3; //div by 8
          var rem = -8 * mod + args.length; //mult add
          fn = Function.pipe(fn, ...args.slice(0, rem));

          while (rem < args.length) {
            fn = fn |> args[rem++] |> args[rem++] |> args[rem++] |> args[rem++] |> args[rem++] |> args[rem++] |> args[rem++] |> args[rem++];
          }
          return fn;
      }
    };
It's a little off topic, but I'd love to see the addition of well-known symbols for all the operators to allow custom overloading. Lua does operator overloading with metatables. No reason JS can't too. At that point, pipe, compose, and even spaceship become much more useful.

    var x = {
      foo: [1,2,3],
      [Symbol.add](rightHandSide) {//Binary
        return this.foo.concat(rightHandSide);
      },
      [Symbol.incr]() {//Unary
        this.foo = this.foo.map(x => x += 1);
        return this.foo;
      }
    }
    //default to standard operator if
    //if left side is primitive or if
    //left side has no matching Symbol
    x + 3; //same as x[Symbol.add](3)

    [0].concat(x++); //=> [0,1,2,3]
    //same as [0].concat(x); x[Symbol.incr]();

    [0, 1].concat(++x); //=> [0, 1, 2, 3, 4]
    //same as x[Symbol.incr](); [0, 1].concat(x);


You can also replace all `a |> b` with a fixed arity `pipe2(a, b)` in your code and again, no new operator needed. Anwyay, I don't think such optimization issues are enough to justify adding this operator to JavaScript. In other functional languages it may make sense, because operators work differently there (e.g. they are interchangeable with functions, there is support for overloading, etc.).

> I'd love to see the addition of well-known symbols for all the operators to allow custom overloading.

Sure, if we had support for first-class overloadable operators then a proposal to add a standard operator like `|>` would make sense. But we don't, so a better one would be to add `Function.pipe` (simple, fits into the language better) or allow operator overloading, etc. (also may be reasonable, but it's a major change in the language).




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: