
F# Pipeline Operator in JavaScript with Babel - galacticdessert
http://codereform.com/blog/post/babel-fsharp-pipeline-operator/
======
zackmorris
Somewhat related, but I wish that more languages provided a reserved
convenience variable for accessing the result of the previous function call:

[https://stackoverflow.com/questions/3326826/language-
history...](https://stackoverflow.com/questions/3326826/language-history-
origin-of-variable-it-in-read-eval-print-loop/3342195)

    
    
      Python and Ruby: _
      Shell: $?
      HyperTalk: it (my first exposure to the concept, even though it was more of a convention)
      Some Lisps: *, **, ***, ...
    

If we had that, then we could write:

    
    
      func1();
      func2(_);
      func3(_);
      ...
    

Which reminds me of PostScript, which lets you examine and manipulate the
program's stack directly (the top of the stack is the previous result):

[http://www.ugrad.math.ubc.ca/Flat/intro.html](http://www.ugrad.math.ubc.ca/Flat/intro.html)

[http://www.ugrad.math.ubc.ca/Flat/stack.html](http://www.ugrad.math.ubc.ca/Flat/stack.html)

If I were implementing it, I would make the previous result and stack contents
read-only so that it's conceptually the same as the pipeline operator except
that it allows inspecting the intermediate value.

~~~
smabie
I really like k/q’s right to left parsing. something like:

f(g(h(x)))

Becomes

f g h x

Which I find very natural and better than

x h g f

Like in a stack language.

~~~
vthriller
> f(g(h(x)))

Haskell has $ operator (simple application operator but with low
precedence—think open bracket that closes implicitly at the end of
expression). While it makes your example a bit more verbose:

> f $ g $ h x

it also allows one to pass multiple arguments in an intermediate function
call, e.g.

> f $ g x $ h y

is the same in Haskell as

> f (g x (h y))

which in most languages would be

> f(g(x, h(y))

~~~
dunefox
Haskell also allows point free style:

    
    
        (f . g . h) x

------
alharith
There's also ReasonML for those looking to scratch the itch sooner.

Also, don't be turned off by the sea of comments. Pick one, they are all
great. but yes ML language enthusiasts can be a bit overenthusiastic, but I
don't fault them for that. They are all anxious to spread the gospel of ML.
The Reason community on discord is excellent, one of the best I have had the
pleasure to be a part of.

I didn't go with straight OCaml with js_of_ocaml because I wanted a better
representation of what I was doing in JS land. There is an excellent write-up
about it here: [https://www.javierchavarri.com/js_of_ocaml-and-
bucklescript/](https://www.javierchavarri.com/js_of_ocaml-and-bucklescript/)

~~~
hopia
And Elm & Purescript.

~~~
KurtMueller
Can't us ML language enthusiasts just all get along? We're all aware of how
vastly superior ML languages are for domain modeling and language readability
:).

~~~
vmchale
As long as you don't use Elm

~~~
KurtMueller
Them's fighting words. I think Elm and F# have the most readable syntax of the
ML languages I've encountered thus far.

~~~
hombre_fatal
The person you're responding to has it out for Elm. I recognize their username
from an Elm thread where they spent their afternoon hating on it with
punchlines like "it's designed for stupid people" and "nobody can take Elm
seriously."

[https://news.ycombinator.com/item?id=17842400](https://news.ycombinator.com/item?id=17842400)
ctrl-f "vmchale"

------
TheAsprngHacker
Why do people credit the pipe operator to F#? OCaml has it too, and F# is
based on OCaml. Did the pipe operator get added to F# first? (The OCaml docs
say that the pipe operator first appeared in OCaml 4.01 [1], and I can't find
out when it got added to F#.)

[1] [https://caml.inria.fr/pub/docs/manual-
ocaml/libref/Stdlib.ht...](https://caml.inria.fr/pub/docs/manual-
ocaml/libref/Stdlib.html)

~~~
tpetricek
The history of how the pipe operator got into F# is documented here:
[https://blogs.msdn.microsoft.com/dsyme/2011/05/17/archeologi...](https://blogs.msdn.microsoft.com/dsyme/2011/05/17/archeological-
semiotics-the-birth-of-the-pipeline-symbol-1994/)

~~~
rattray
Sounds like the idea came up April/May 1994 for F# then. Not sure when the
feature landed.

Was added to OCaml 2013-09-12[0]

[0]
[https://ocaml.org/releases/4.01.0.html](https://ocaml.org/releases/4.01.0.html)

------
tombert
It's times like these that make me realize how spoiled I am by
ClojureScript...the -> and ->> macros basically give me a pipe operator, and
have been in the language from its inception :)

I'm actually glad that this kind of composition is growing in popularity. Back
when I did F# for a living, I loved that by using the pipe operator, you could
get something more or less akin to a fluent interface, without any direct
coupling between the two composed functions, and without any gross
intermediate variables. I have nothing against "regular" point-free
composition or anything like that, but I do think that these pipe operators
are easier to digest for a lot of purposes.

~~~
bjoli
Those macros are just that, macros. Implementing them in any sexp-based
language is trivial.

I think people are stuck in the "C MACROS ARE BAD" thinking. It is a really
powerful feature. CLOS, the world's most flexible oo system, was conceived as
a bunch of macros. Racket's pattern matching is really just a macro (that
could almost be called a compiler). Useful things that can be syntactically
integrated into a language as an afterthought, yet still look and feel first
class.

~~~
tombert
No argument here; I'm a pretty big supporter of Lisps as a whole; most of my
personal projects are done with Clojure (or ClojureScript) in no small part
due to the power afforded to me by macros.

In fairness to a lot of people, I think that most of the folks in the "MACROS
ARE BAD!!!" crowd are under the impression that C macros are the only way
people do them, and assume that even Lisp macros are just are also just
glorified text-expansion. I would be against macros too if that were the case.

~~~
kazinator
I suspect that most of "MACROS ARE BAD!" folks only know Python or Java, and
have never used a macro preprocessor, just like the "COBOL IS BAD!!" people of
the past who had zero experience with Cobol.

> _I would be against macros too if that were the case._

I wouldn't; even text expansion macros are useful, and can be done better than
what is in C.

Using C preprocessing as an example criticize only textual/token macro
expansion is still a strawman.

------
adgasf
I find JavaScript unusable without this operator. Fortunately Babel supports
it well.

It makes a great alternative to fiddling with prototypes or wrapper objects
when you want to extend something.

For example, this is flat-map implemented as a free function:

    
    
        const flatMap = f => {
          if (!f) {
            throw new TypeError('f must be a function');
          }
    
          return xs => ({
            [Symbol.iterator]: function * () {
              for (const x of xs) {
                yield * f(x);
              }
            }
          });
        };
    
    
        // Usage
        const xs = [ 1, 2, 3 ] |> flatMap(x => [ x, -x ]);

------
lf-non
For people who want something similar that is also typescript friendly, I
wrote a babel macro that entirely gets compiled away during build.

[https://github.com/ts-delight/pipe.macro](https://github.com/ts-
delight/pipe.macro)

------
jiofih
That fetch / await example is horrendous. Please don’t.

------
lwb
Anyone else think the async/await example looks terrible? Why do the keywords
have to be on their own line?

------
egeozcan
I had created a solution to scratch my itch while waiting for the pipe
operator which even got a logo when it got more than a hundred stars, for
anyone interested:
[https://github.com/egeozcan/ppipe](https://github.com/egeozcan/ppipe) (needs
ts typings though)

~~~
svrtknst
This is a neat one. In my current project, I created a (non-lazy) pipe
function that take a value and a bunch of functions, composes the functions,
and applies the value.

pipe(2, add(2), square, n => n + 7) // -> 23

Nothing fancy but comes in handy at times.

What I really miss is something like the |> from Elixir. Maybe it's the same
as F#, I don't know, but it automatically performs partial application, so
that:

Enum.filter([1, 2, 3, 4], fn n -> n % 2 == 0 end)

also works like:

[1, 2, 3, 4] |> Enum.filter(fn n -> n % 2 == 0 end)

~~~
egeozcan
My library adds the piped value as the last parameter but if you read the
docs, you'd see that you can use a placeholder to change its place:

    
    
        ppipe([1, 2, 3, 4]).pipe(Enum.filter, _, x => x % 2 === 0)
    

Not that filter fn in js is in the prototype of arrays so you'd need to
extract it. There's a shortcut when using my library though:

    
    
        ppipe([1,2,3]).filter(x => x > 2)
    

I made the practical decision to provide prototype functions from the piped
value while chaining.

------
koboll
This is nice syntax, but since there are two competing proposals and they're
collectively at stage 1, this isn't something anyone should really be using
for nontrivial code, since there's a good chance either way that you'll be
writing JavaScript that will never be valid.

------
IggleSniggle
I know this proposal has been sitting around forever, but it really can't come
fast enough imho. I don't know how to help show my support for its inclusion,
however. Can anyone help elucidate?

~~~
IggleSniggle
I decided to answer my own question and visit the tc39 website. The lazy
answer is that you must be part of a company that is willing to pay ~$70000 to
have a voice, and have access to your companies representative to tc39. There
are other categories of members (like Mozilla), but here's the short list of
"Ordinary members." More info at [https://www.ecma-
international.org/memento/members.htm](https://www.ecma-
international.org/memento/members.htm)

If you are part of one of these companies and interested in this proposal,
it'd be super cool to see who you need to talk to to influence this decision.

Facebook

Google

Hitachi

IBM

Intel

Konica Minolta

Microsoft

PayPal

Stripe, Inc.

~~~
gambler
It's worth reflecting on why the most popular language in the world has syntax
so inflexible that a simple operator like this requires begging some ubercorp
for a compiler change. Smalltalk had user-definable binary/infix operators in
1980. They didn't require you to muck around with the compiler at all.

Ironically, JavaScript has the extensibility mechanisms and object-oriented
features inspired by Smalltalk that _could_ allow for pretty much the same
thing. However, most of the community doesn't seem to even realize that this
is a possibility.

~~~
TomMarius
It's worth saying that mostly the same companies are building WebAssembly as a
solution to this problem.

~~~
xkriva11
Really? If you would try to do an effective Smalltalk implementation in
WebAssembly, you would immediately see that WebAssembly is making things even
much worse.

------
vmchale
F# pipeline operator aka... function composition that has existed in other MLs
forever?

~~~
galacticdessert
Value of your comment being? It does not matter where it comes from, just that
is cool, useful, readable and it might be a great idea to add it to JS, same
as it is already in f#, oCaml, Elixir, etc etc etc.

~~~
mrkeen
I miss it when it's not there.

This is currently #5 on HN: [https://www.infoq.com/articles/java-14-feature-
spotlight](https://www.infoq.com/articles/java-14-feature-spotlight)

It's a pretty long read and could have done with some precedent.

------
waylandsmithers
I enjoy the utility of this operator in Elixir but I'm not sure how I feel
about including it in JS.

My fear is that JS is trying to be all things to all developers- it feels like
we just added the `class` keyword for the OO inclined and now we're moving
towards functional programming? Do we really want to move further away from
the idea of idiomatic JS?

But who knows, maybe it will open the door to massive productivity and
enjoyability gains with the language...

~~~
hajile
Classes require tons of work under the hood to work. In exchange, they add a
confusing new concept of fields alongside protypes while also hiding the
actual prototype inheritance to ensure future confusion.

The pipe operator is a super-basic transformation of the AST. In exchange, you
get much easier to read "nested" functions. `foo(bar(baz(x))) --> x |> baz |>
bar |> foo` is very easy to teach and very easy to understand.

It's hardly a fair comparison.

------
blunte
What was the author thinking with that pointless meme image?

~~~
why-oh-why
Oh my god, we should never allow people to have a little fun on their own blog
and if they do, let’s call them childish and unworthy of finding work. /s

It’s just a meme, used correctly, what’s the problem? This isn’t the White
House’s blog.

~~~
blunte
You said a lot of words that I did not say...

To me, that image was superfluous and likely to perpetuate the "boys club"
appearance of tech.

Sure, the author can put whatever they want on their site. But why go to the
trouble of writing something and then tossing in a Piss Off message to some of
the readership?

------
kummappp
if you add multiplication functionality, identity and termination symbols to
it, you get something that is closer to a proper category like done in this:
[https://github.com/kummahiih/python-category-
equations](https://github.com/kummahiih/python-category-equations)

f1(?) |> ( f2(?), I ) |> f3(?) == f1(?) |> f2(?) |> f3(?) , f1(?) |> f3(?)

it just feels natural that way

------
WouterSpaak
Fun fact: the `pipe` function from `rxjs` can be imported on its own where it
will work on any value of type T, not just `Observable<T>`.

------
thibran
I wrote something similar for Scheme -
[https://cons.io/reference/sugar.html#chain](https://cons.io/reference/sugar.html#chain).
It's interesting to see that other people came up with a quite similar
solution, providing two modes: pass-directly, pass-by-variable. Does the JS
version supports destructuring?

~~~
bjoli
I have a similar scheme thing, but that does implicit left or right insert
when no <> is present and has an apply <...>:

[https://bitbucket.org/bjoli/guile-threading-
macros/src](https://bitbucket.org/bjoli/guile-threading-macros/src)

I wrote it when I was a beginner at syntax-rules macros, but it gets the job
done. It doesnt do destructing though!

~~~
thibran
Your version is interesting too. For a while I also thought about the stop-
when-false idea, but came to the conclusion that it should probably be another
macro. Building an enough powerful, but easy macro, isn't that simple.

------
reaktivo
I think it's worth nothing that a full Promise handling pipe function can be
written like so:

    
    
        const pipe = (...args) => args.reduce(async (acc, fn) => fn(await acc));
    
    

No actual third party libraries required

------
platz
It's nice that you can't typo your variables when writing point-free code

------
chaorace
For those of you who work with Node, highland.js is another great route for
composable programming. It has excellent support for Node streams, which has
made it quite handy with Gulp especially!

------
bjoli
The 5 codebox does filter map reduce. That makes me wonder, is there any loop
fusion going on in JS? Or are there any good libraries for transducers?

------
seanwilson
What happens when you need to debug and see how the data looks at each stage
of the transformation pipeline? Won't the debugger show this as a single step
if you use the pipeline operator? It does look cleaner and means you don't
have to spend time coming up with names for the intermediate results though.

~~~
WorldMaker
The pipeline is still a set of function calls, despite the syntax sugar. You
can think of x |> f |> g as g(f(x)). Even when written as a single line, a
good debugger will let step through each function one at a time (as that is
effectively how the system operates), and a great debugger will help you set
breakpoints even "inside the line" to a specific point.

(Many debuggers already support that last bit of inserting breakpoints inside
of lines instead of "at" lines, it's just not as obvious as the usual "click
the spot to the far left gutter of the line you want". Sometimes it is
something like a Right-Click on the specific part of the line and look for a
command such as "Insert Breakpoint Here". It's something to learn if your
chosen debugger supports today.)

The obvious tangent here, of course, is that many times when you might want to
heavily use something like a pipeline operator you are likely working with
something more abstract between function calls like an Iterator/Generator
pattern (or an Observable pattern), and debugging those require different
habits/tools as the "intermediate results" aren't directly interesting (an
Iterator is "just" an object with a next() function, it's not an array of
intermediately processed data). Learning to debug those patterns is its own
skillset (whether or not you are using a pipeline function or a pipeline
operator or old-fashioned function call syntax), but one common example is a
function you'd add inside the pipeline often called something like a "tap". In
that case you might "tap" in to the middle of a pipeline to log intermediate
results. Something like:

    
    
        x |> f |> tap(y => console.log(y)) |> g

------
sbussard
Why not use the haskell bind operator? They're similar, right?

~~~
mrkeen
closer to compose (.) than bind.

    
    
      f .  g = \x -> f (g x)
    
      f |> g = \x -> g (f x)

~~~
lgas
It's

    
    
        flip ($)

~~~
hopia
import Data.Function (&)

------
dimgl
Thanks, I hate it.

