
The JavaScript Pipeline Operator - yanis_t
https://yanis.blog/the-pipeline-operator-in-javascript/
======
mbostock
You don’t need lodash or a new operator for the example provided:

    
    
      departments
        .flatMap(d => d.employees)
        .map(e => e.salary)
        .reduce((p, v) => Math.max(p, v), 0)
    

That said, the pipe operator is nice because it allows similar chaining
patterns on subjects that are not arrays, as shown in the proposal’s README:
[https://github.com/tc39/proposal-pipeline-
operator/blob/mast...](https://github.com/tc39/proposal-pipeline-
operator/blob/master/README.md#introduction)

~~~
yanis_t
Good point. Is flatMap already supported by browsers or is it a proposal?

~~~
porphyrogene
Both flat[1] and flatMap[2] are unsupported in Edge, IE and Node.

1\. [https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat#Browser_compatibility)

2\. [https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap#Browser_compatibility)

------
phillipcarter
If you want to see how the operator "feels" in practice, I recommend trying
out F#[0] or Elixir[1]. Hint: it's pretty great!

[0]:
[https://www.microsoft.com/net/languages/fsharp](https://www.microsoft.com/net/languages/fsharp)

[1]: [https://elixir-lang.org/](https://elixir-lang.org/)

------
icholy
ES6 was needed, but I think js should hit the breaks for new language features
for a while.

~~~
coltonv
I disagree. ES6 made JS much more palatable but it's still behind most modern
languages in terms of number of features _and_ in velocity of new features
getting introduced. Javascript has actually been extremely slow to get new
features compared to other languages.

Since ES6 (3 years ago), the only major, mainstream feature that has been
added to JS has been async functions, which are awesome, but in that time
Python (a language of similar age and global use) has gotten an optional type
system, async/await, and _hundreds_ of standard library modules, classes, and
functions.

In my opinion, JS badly needs more functions in it's standard library, which
should move through the proposal policy quickly. However, common sense
standard library functions like flatMap have been in the pipeline for _years_
are still not in the final stage. We may not get flatMap, import(), trimStart,
or trimEnd in ES2019, they are all stalled in Stage 3. There's no good
arguments not to have these in the standard library, but we still have to wait
years.

In addition to that, JS could really use some language features that make it's
core paradigms, functional and object oriented, simpler, and have been in
other languages for years. The pipeline operator would make functional
programming much easier, and decorators would make object oriented programming
easier. These are, in my opinion, common sense features since they are so well
received in other langauges, but they get stalled out since they are seen as
"bloating" the syntax, or making javascript "too opinionated on how to do
something". Meanwhile, people are compiling full fledged functional languages
(Reason, Elm, Purescript) to Javascript, because Javascript isn't keeping up
with them on features. It could, but the TC39 review process is so slow that
it's not worth waiting on. For those reasons, in my opinion, we should really
be releasing new language features much quicker.

~~~
hajile
> I disagree. ES6 made JS much more palatable but it's still behind most
> modern languages in terms of number of features and in velocity of new
> features getting introduced. Javascript has actually been extremely slow to
> get new features compared to other languages.

JS has been adding features at a tremendous rate. Several major features land
every year. Adding something like async generators is a huge change that
affects large parts of the language.

If the Go, Ruby, or Python guys want to add a feature, they just code up a
proposal and add it if the maintainers like it. JS has 4 major implementations
and a dozen or so other reasonably popular implementations. Adding a feature
in a way that works with all of them (without breaking 25 years worth of
applications) is hard.

Reason, Elm, and Purescript are _not_ good comparisons. The features they add
(especially the type system) aren't likely to ever be JS features (if they are
even possible in the language).

~~~
coltonv
You say "Several major features land every year.", but you don't back that up.

In 2018 the major features we got object rest/spread and asynchronous
interation. Rest/spread is a big feature that I forgot to mention, but async
iteration is only useful in a few situations, I'd call it a minor feature.

In 2017 we got async/await, and shared memory/atomics. Only a few fringe code
bases will use shared/memory/atomics, so I'd call async/await the only major
feature.

In 2016 we got 0 major features. The only things in the entire spec (an entire
year of language progress) was Array.includes and an exponent operator ( __).
Both of those are very clearly "minor" features.

So in the last 3 years, I'd say we've had 2 or 3 major, mainstream features,
that's very far from "several major features every year". Especially 2016
where there were 0 major features.

~~~
hajile
In 2016, browsers were still working on implementing es2015 (I'm still waiting
for proper tail calls on non-Safari engines).

Atomics is a huge feature (especially in the amount of work required).
getOwnPropertyDescriptors is also a big addition.

Async iteration is a very big deal that can potentially affect things like
reading files in node. Object spread seems big, but is actually far more
simple as it is a syntactic special case of Object.assign(). In contrast, the
far reaching repercussions of adding async iteration and the implementation
are both large things. Given the level of optimization for JS regex, adding a
bunch of new features there is also a big job. Promise finally is also a big
update (though combining map and flatmap in promises automatically is the
biggest issue with them).

This year, there's integers, working imports, and big class upgrades in the
works. The list of major proposals is rapidly shrinking.

Is there another major language adding that many big features in the past
three years? Keep in mind that most stage 3 proposals already have at least
one implementation already done.

------
furgooswft13
Been using this in LiveScript for awhile (ls also provides it in the opposite
direction <| ). Very useful for chaining functions in a logical order and
without unnecessary () nesting. Absolutely needs to be paired with partial
application though for multiple argument functions.

~~~
yanis_t
Yeah, partial application is also a stage 1 proposal now. But for now I guess
you could also do with auto-curring functions
([https://ramdajs.com/](https://ramdajs.com/) or something like that)

------
AdieuToLogic
FWIW, several language communities call this "the Thrush combinator"[0]. Armed
with that term, searching the web for background/info/theory on this topic
will be easier.

0 - [https://debasishg.blogspot.com/2009/09/thrush-combinator-
in-...](https://debasishg.blogspot.com/2009/09/thrush-combinator-in-
scala.html)

~~~
yanis_t
Thanks! Sounds a bit too scientific to my ear but good to know

~~~
AdieuToLogic
It is what it is, and what it is in JavaScript is this:

    
    
      Object.prototype.pipeTo = function (functor) {
        return functor (this);
        };
    
      function inc (input) { return input + 1; }
    
      // this will write out "2" to the console log
      new Number (1).pipeTo (inc).pipeTo (console.log);
    
    

HTH

------
theLotusGambit
Interesting. This proposal would for the first time allow any javascript to be
written in just 5 characters:

    
    
      []+|>
    

Down from the 6 that jsfuck requires:

    
    
      []+!()

------
gedy
> "Expressions Math.sqrt(64) and 64 |> Math.sqrt are equivalent. The
> difference is in how we read it. With pipeline operator data flows from left
> to the right, and thus making it much more comprehensible"

Ugh, no, that's a very subjective statement, 64 |> Math.sqrt is not "much more
comprehensible" assuming especially for beginners.

~~~
williamdclt
I agree that this example isn't convincing, I also prefer the "vanilla"
Math.sqrt(64).

It's more convincing (IMO) when chaining calls, eg:

Math.sqrt(Math.ceil(Math.sin(64))) VS. 64 |> Math.sin |> Math.ceil |>
Math.sqrt

~~~
mercer
I used to feel that way until I got comfortable with Elixir and its pipe
operator. I think without prior knowledge the latter, which is interpreted
left-to-right, is more intuitive: "take some data, do x, do y, do z"

------
nabla9
>The difference is in how we read it. With pipeline operator data flows from
left to the right, and thus making it much more comprehensible without the
need to introduce extra variables.

Why you need operator? Can't it be done with a function?

    
    
        pipe(64,Math.sqrt)

~~~
weliketocode
Your example falls apart when making multiple function calls.

~~~
nabla9
Please explain.

~~~
Spivak
64 |> Math.sqrt becomes pipe(64, Math.sqrt)

64 |> Math.sqrt |> Math.sin becomes pipe(pipe(64, Math.sqrt), Math.sin)

Assuming pipe was smart and took varags we can make this better.

64 |> Math.sqrt |> Math.sin becomes pipe(64, Math.sqrt, Math.sin)

~~~
hajile
A pipe operator compiles directly into the AST and completely disappears
before execution.

    
    
        64 |> Math.sqrt |> Math.sin
        Compiles in the AST to
        Math.sin(Math.sqrt(64))
    
        //curries variables away from functions
        var realPipe = (a, ...args) => (...initParams) => {
          var acc = a(...initParams);
          for (var i = 0; i < args.length; ++i) {
            acc = args[i](acc)
          }
          return acc;
        }
    
        //if first parameter is a function, curry to delay execution
        //if first param is not a function, curry, but call immediately
        var pipe = (a, ...args) => 
          (typeof a === "function")
            ? realPipe(a, ...args)
            : realPipe(...args)(a)
    

I'd note that this pipe implementation also bleeds its abstraction. JS has
first-class functions, but this will never treat a function as data. You must
be aware of how it works so if you are passing a function (for example, a
getter function), you must manually curry it afterward.

This complication is why most pipe implementations don't actually mess with
that data attribute part (they just skip to the realPipe implementation), but
since the pipe operator can handle it, I figured I'd try to as well to show
the issues.

~~~
Spivak
I see no reason our function form couldn't do the same magic behind the
scenes. Really no different from a lot of C compilers in this respect and with
the bonus of an absolutely trivial polyfill.

------
Prefinem
Piping is relatively easy to write a function for. Here [0] is a package that
I wrote that has a few trivial functions (including pipe) for things such as
this.

0: [https://www.npmjs.com/package/afpf](https://www.npmjs.com/package/afpf)

------
Scarbutt
He was whining about lodash but still used it till the end.

His lodash and pipeline operator version looked more noisy than the lodash
only version.

------
tracker1
My only real criticism, is it would be nice if it supported
generators/promises as an alternative to for-await syntax.

~~~
WorldMaker
IxJS [1] already has pipeable functions for working with AsyncIterable (the
underlying interface for the proposed for-await-of syntax).

[1] [https://github.com/ReactiveX/IxJS](https://github.com/ReactiveX/IxJS)

------
sAbakumoff
rxjs is already available and allows to perform chained transformations by
using wide variety of methods.

~~~
yanis_t
Good point. I've heard about it but never could quite understand if it's
useful enough. Can you share some public repos where it's being used?

~~~
sAbakumoff
the official web-site is pretty informative and has a ton of
samples..[https://rxjs-dev.firebaseapp.com/api](https://rxjs-
dev.firebaseapp.com/api) \- take a look at operators section for example. as
for public repos - nowadays any modern front-end app that is built with
Angular is using rxjs - it's de factor standard for data processing in angular
world.

~~~
yanis_t
Thanks sir. Will take a look

------
rudolph9
You could just use ramda
[https://ramdajs.com/docs/#pipe](https://ramdajs.com/docs/#pipe)

Not quite as seamless and it would be nice to have |> but ultimatles
introduces a lot of complexity.

The only good reason I could see is if there is a performance benefit a
JavaScript JIT could take advantage of by having the |> operator?

~~~
yanis_t
Honestly, I don't think so. More likely this will be just a syntactical sugar.

~~~
WorldMaker
Performance benefits likely depend on which side of the partial application /
currying coin the operator ends up landing on. Some of the partial application
proposals would potentially reduce the needs for currying in the language,
which would reduce the needs for a lot of variable closures in some libraries.

The biggest potential benefit is to typing (in Typescript or Flow), which
potentially can provide potentially much better or at least much simpler
typing and type inferencing with an operator versus many of the alternatives
(such as variadic pipe() functions).

