Is this:<p><pre><code> Object.keys(envars)
.map(envar => `${envar}=${envars[envar]}`)
.join(' ')
|> `$ ${%}`
|> chalk.dim(%, 'node', args.join(' '))
|> console.log(%);
</code></pre>
Really better than:<p><pre><code> console.log(chalk.dim(
`$ ${Object.keys(envars)
.map(envar => `${envar}=${envars[envar]}`)
.join(' ')
}`,
'node',
args.join(' ')
));
</code></pre>
That's the real-world example they have (I reformatted the second one slightly, because it looks better to me). Neither seems very good to me, and the |> version doesn't really seem "less bad".<p>Can also write it as:<p><pre><code> process.stdout.write(chalk.dim(
`$ ${Object.keys(envars)
.map(e => `${envar}=${e[envar]}`)
.join(' ')
}`,
))
console.log(chalk.dim('node', args.join(' ')))
</code></pre>
Which seems clearer than either because it splits out "print env variables" and "print out node args". And it would be even better with some sort of helper to convert an object to k=v string:<p><pre><code> console.log(chalk.dim(`$ ${dumpObj(envars)}`, 'node', args.join(' ')))
</code></pre>
---<p>I also feel this:<p>> In the State of JS 2020 survey, the fourth top answer to “What do you feel is currently missing from JavaScript?” was a pipe operator.<p>Is the wrong way to go about language design. Everyone wants something different, and if you just implement the "top 5 most requested features" you're going to end up with some frankenbeast of a language.
Temporary variables are often tedious? I have found that well named temporary variables are the only clear way to comment code without actually writing the comment. The version with temporary variables is much easier to understand without having to read the rest of the code.
I hope Records & Tuples[0] land before this does. It would have meaningful and far reaching positive effects for the language, without much controversy. Like most of these things, it takes about 5-7 years for it to permeate through enough of the engines to be meaningfully useful in the day to day of web developers (node / deno typically 12-18 months tops). It would drastically speed up existing code once wide adoption is gained though.<p>I don't think the Pipe Operator would be as useful in comparison.<p>I really hate how long the Records & Tuple proposal has been languishing at stage 2. It could've shipped <i>years</i> ago if not for a syntax debate that dragged on and on without being fruitful[1]<p>EDIT: there is a class based version of this, that is stage one, for adding structs[2]. They behave similarly.<p>[0]: <a href="https://github.com/tc39/proposal-record-tuple">https://github.com/tc39/proposal-record-tuple</a><p>[1]: <a href="https://github.com/tc39/proposal-record-tuple/issues/10">https://github.com/tc39/proposal-record-tuple/issues/10</a><p>[2]: <a href="https://github.com/tc39/proposal-structs">https://github.com/tc39/proposal-structs</a>
Saw a talk with Douglas Crockford[0] years ago. He said something like: Before JS classes got introduced he asked why they didn't just implement macros for the language. Classes are in fact just syntactic sugar. Just like async/await, and now this proposal.<p>In hindsight he was right. JS would be better off if it did have macros. Much of the whole babel/webpack/react/ts stuff would be just a bunch of macros instead of idiosyncratic build tools and so on. And we would have had much less compatibility churn.<p>In fact this proposal here, is trivial to implement with macros. Clojure has the same thing (threading operator) and it's just a macro.<p>[0] <a href="https://en.wikipedia.org/wiki/Douglas_Crockford" rel="nofollow">https://en.wikipedia.org/wiki/Douglas_Crockford</a>
I don't understand why you can't just use temporary variables. The article mentions mutation is bad, but what actually happens is that the name gets reassigned. No <i>value</i> is mutated.<p>That brings me to something I really want in JS, actual unmutable values. If you use `const x = new SomeClass()`, you cannot reassign it, but you <i>can</i> change fields. The first time I encountered `const`, I thought it did the opposite. It would be cool if you could declare something (object, array) to be an immutable value.<p>If you really want to introduce new operators, how about operator overloading? For example vector and matrix calculations become a lot clearer and less error-prone with infix operators. It should be technically easy to add them to typescript - rewrite the expression to a function call depending on the types of the operands - but the TS devs refuse to implement this on philosophical grounds unless it is implemented in JS. I guess in JS it would require runtime dispatch, but maybe that is not such a big penalty given that it usually uses a JIT anyway.<p>Oh, and while we are at it, fix `with`. The JS with statement is ridiculous and deprecated anyway. It makes <i>all</i> fields of an object available in it's scope. Contrast with VB6's `with`, which requires you to use a leading dot and is much more readable:<p><pre><code> with (elem.style) {
.fontFamily = 'Arial';
.color = 'red';
console.log(.borderWidth);
// in actual JS this would just be
// console.log(borderWidth);
}</code></pre>
Can't wait for this. Pipes are awesome in Elixir and bringing them to JS/TS will be great.<p>To me this is both concise and readable:<p><pre><code> const weather = `https://api.weather.gov/gridpoints/TOP/31,80/forecast`
|> await fetch(%)
|> await %.json()
|> %.properties.periods[0]</code></pre>
I *<i>HATE*</i> pipes. For example from Elixir School (<a href="https://elixirschool.com/en/lessons/basics/pipe_operator" rel="nofollow">https://elixirschool.com/en/lessons/basics/pipe_operator</a>):<p>```
foo(bar(baz(new_function(other_function()))))
```<p>They offer this example of an improvement:<p>```
other_function() |> new_function() |> baz() |> bar() |> foo()
```<p>While yes, pipes improve readability, how do they deal with errors? How do they deal with understanding what each thing is supposed to return?<p>I would prefer something like this, (descriptive variable names):<p>```
var userData = other_function();<p>var userDetails = new_function(userData);<p>var userComments = baz(userDetails);<p>var userPosts = bar(userComments);<p>var finalUserDetails = foo(userPosts);<p>return finalUserDetails;
```<p>Then I can easily debug each step, I can easily understand what each call is supposed to do, if I'm using type script, I can assign types to each variable.<p>I strongly oppose clean code for the sake of looking pretty, or being quick to type. Code is meant to be run and read more then written, it should be descriptive, it should describe what it's doing not a nasty chain of gibberish. Hence why most people hate REGEX.
These hack pipes are a trojan horse. People wanted elixir/F#/ocaml aka function pipes, and what we got was unreadable line noise. I argued against it until I was blue in the face, decided it was bad for my general wellness to keep it up. I genuinely would prefer no pipes over this. I couldn't find a single example where I preferred it. The token they chose already has a meaning in Javascript! The arrogance and willful disregard for readable code was astonishing. The only tangible reason I could pull as to why they picked the least popular implementation despite all the outrage was "someone at google didn't like the function pipes". Even if you think we should avoid it because some google employee doesn't want it, that doesn't mean you should ram in an even worse implementation. I had to block the TC39 discussion because I was just going to get argumentative because they weren't listening at all, and they were dismissing actual concerns without any explanation.
This strikes me as something better left to libraries. If you want to write in a functional style then Ramda, Lodash, Underscore, and plenty of others have pipe and compose functions.<p><pre><code> pipe(one, two, three)
</code></pre>
Easy to read. No new syntax. Extendable with arrow functions.<p>Yes, there are some limitations in comparison to Hack Pipes. But those are far outweighed by not messing yet again with the language’s syntax.
Can someone remind me again why there's never been movement to add a second modern language to web-browsers? JavaScript was created in a weekend and then stuff tacked on for the last 28 years.<p>We know so much more about how to create programming languages today than we did then, and the whole "Year of the Linux Desktop" has become a WebAssembly meme now every year since its introduction six years ago, with it getting popular always next year, next version, with feature XYZ. Seemingly creating unmaintainable/debuggable mess from external languages with no true 1:1 into WebAssembly isn't as big of a hit as the originators expected.<p>Yet every time someone asks why there hasn't been movement here it is "Year of WebAssembly is next year!!!" WebAssembly has managed to slow actual progression towards something good. With the browser monoculture you'd think it would be easier now than ever to start a fully integrated second language with WebAssembly compilation for backwards compatibility.
For languages, which do not have built-in the power to change themselves, in many cases it might be better to stick to their feature set, instead of introducing even more language concepts. Look at how much work is involved to get something as simple as pipelines. As if they will ever find the right syntax for everyone.<p>If we used a normal function we might have to include a library or a dependency or heck, just take 5 minutes and write a pipeline function oneself. Sure, it will not be syntactically as minimal as a _change of the language itself_ to allow pipelining, but at least it will not introduce even more language features and everyone can easily look at the definition change it or include it in their own projects.<p>Ideally we would strive for a minimalistic set of features, which in turn can implement anything we want. Adding more and more features, without introducing a way for the user to modify the language without a commitee and a lengthy process, seems short-sighted.<p>If you want to give the user more power over syntax, introduce a well engineered macro mechanism (plenty of examples in other languages) and let people develop standards and libraries as macros and libraries. Later on decide what to take into the standard language. Similar to how jQuery influenced modern JS standard functions like querySelectorAll. Even if you don't take something into the standard language, users are still free to include it in their project. No harm done and everyone gets their lunch and can scratch their itches.
As someone who actually loves JavaScript, I think this is a really bad idea.<p>Maybe it has a place in other languages. I really don't want to see it in JS. We don't need more ways to do things implicitly or bass-ackwards from how they're actually behaving underneath. Syntactic sugar rarely makes code more readable. This operator only makes code seem more concise and as if it's executing in a way that it actually isn't.<p>I can absolutely see junior developers running wild with this kind of thing.<p>JS should be kept <i>simple</i>. This operator is not simple. It now makes understanding expressions more complicated. JS has its quirks and rough edges, but its power is in its simplicity. Please do not import the mistakes of other languages into JavaScript. If someone wants this operator, they should be forced to use a Babel transform or to compile their own JS interpreter.<p>OR just compile your favorite language runtime with the pipe operator to WASM.
An alternative is to make the pipe operator a simple function application and provide syntax for creating simple pipeline functions.<p>For example:<p><pre><code> left |> right
</code></pre>
Would semantically translate to:<p><pre><code> right(left)
</code></pre>
And you could define a pipeline function like so, where the following:<p><pre><code> const myPipeline = @[
one(@),
@.two(),
@ + three,
`${@} four`
]
</code></pre>
Would translate to:<p><pre><code> const myPipeline = (value) => {
const _1 = one(value);
const _2 = _1.two();
const _3 = _2 + three;
const _4 = `${_3} four`;
return _4
}
</code></pre>
Or:<p><pre><code> const myPipeline = (value) => `${one(value).two() + three} four`;
</code></pre>
And you could define the placeholder value name (which would allow nesting):<p><pre><code> const myPipeline = @it [
one(@it),
@it.two(),
@it + three,
`${@it} four`,
]
</code></pre>
You'd combine the two syntaxes to get immediately-invoked pipeline functions:<p><pre><code> // Using a modified example from the proposal:
envars |> @ [
Object.keys(@),
@.map(envar => `${envar}=${envars[envar]}`),
@.join(' '),
`$ ${@}`,
chalk.dim(@, 'node', args.join(' ')),
console.log(@),
]
</code></pre>
This is better, in my opinion, than building the '%' placeholder syntax into the pipe operator.
That % syntax is just completely unlike anything else I have seen in JS.<p>As a multi paradigm language, JS typically suffers from whatever programming style is on trend when these features are implemented. We are apparently on the other side of the pendulum now, but I can’t remember the last time I worked with a class and felt like that was right either.
This just complicates things if you have any kind of complex nesting.<p>Something "simple" like:<p><pre><code> a(b(),c(),d(e(),f(g())))
</code></pre>
Turns into the following:<p><pre><code> value |> b() |> a( %, c() , v2 |> e() |> d(%, v1 |> g() |> f(%) ))</code></pre>
Cool stuff, but I miss the "it" variable from HyperTalk (the language used by HyperCard) which contained the result of prompts to the user. Just search for "The it variable":<p><a href="http://www.jaedworks.com/hypercard/HT-Masters/scripting.html" rel="nofollow">http://www.jaedworks.com/hypercard/HT-Masters/scripting.html</a><p><pre><code> ask "How many minutes do you want to play?"
put it * 60 into timeToPlay -- convert it into seconds
</code></pre>
Today we could have a reserved keyword that holds the result of the last statement executed. A practical example adapted from the article using "it" might look like:<p><pre><code> Object.keys(envars)
it.map(envar => `${envar}=${envars[envar]}`)
it.join(' ')
`$ ${it}`
chalk.dim(it, 'node', args.join(' '))
console.log(it);
</code></pre>
A better name for "it" today might be "_", "result" or perhaps '$' in a shell-inspired language like php. Most shells support "$?" so that could work too:<p><pre><code> # prints 0
true ; echo $?
# prints 1
false ; echo $?</code></pre>
I wonder if "%" placeholder is the right approach. It makes the code longer in most cases.<p>Without pipes:<p><pre><code> a = d(c(b))
</code></pre>
With pipes in the proposed form:<p><pre><code> a = b|>c(%)|>d(%)
</code></pre>
My first approach to design it would be:<p><pre><code> a = b~>c~>d
</code></pre>
So the rule is that on the right side of the pipe operator (~>) there is always a function. We don't need parenthesis to indicate that.<p>If the function takes more than one argument, it can be defined by another value on the left of it.<p>Without pipes:<p><pre><code> a = d(c(b,7))
</code></pre>
With pipes in the proposed form:<p><pre><code> a = b|>c(%,7)|>d(%)
</code></pre>
With the ~> approach:<p><pre><code> a = b,7~>c~>d</code></pre>
> three(two(one(value)))<p>const oned = one(value);<p>const twoed = two(oned);<p>const threed = three(twoed);<p>This proposition goes out of its way to find problems with code that is written in a confusing and uncommon way in the first place.
The proposed pipe operator could only be efficiently implemented via a transpiler pass to lower it to "regular" JS varaibles. I wouldn't want this feature in a JS engine due to the overhead it would require. Sometimes it's better just to say no to new features that yield questionable utility and don't reduce code size or complexity by much.
I love the pipe operator in Elixir, but I've never really wanted it in JS. It's critical in Elixir because the design of the runtime (immutability, functions only, no methods). In the rare cases that you might need it, like their three(two(one(value))) example, function composition is available from libraries or easy to do yourself.<p>It's just my opinion, but I think turning JS into a kitchen sink of language features is a mistake.
I know I'm in the minority, but I dislike both version of the syntax, since I think chained code is almost always worse than just being forced to use intermediary variables for everything.<p>While it's fine to say Object.entries().map(), anything more is not only harder to read, but makes debugging and maintenance more difficult.
Certainly looking forward to the pipe operator - if it ever lands! It doesn't seem like it's moved to the next stage recently in the commit history, and it's been discussed for ages now.
The pipe operator is awesome because you can use it to “extend” objects without messing with their prototypes.<p>Missing String.titleCase ? Write your own!<p><pre><code> “hello world” |> titleCase</code></pre>
> Deep nesting is hard to read […] Temporary variables are often tedious<p>I’m a little torn because pipes might be pretty nice, especially for prototype code and small projects. One thing this proposal doesn’t acknowledge is that for production code, deep nesting’s often impractical and often considered an anti-pattern in the first place, so making it easier isn’t a common need or problem to have in my experience. Usually I’m going the other way, having to make it more tedious. Using temporary variables and breaking apart nesting is, far more often than not, necessary in order to do proper error checking, and just to make code readable, commentable, and refactorable, etc. I feel like what we need is not a way to make deep nesting easier to read, it’s a way to make temporary variables less tedious, perhaps while also piping from one function to the next… <i>that</i> would be really helpful in a deeper way than just adding another chaining syntax. Is the syntax is stuck at “|>”? I guess it’s not possible to override bitwise-or (“|”), but “|>” feels maybe a little clunky?
At work I am always hopping between F# and JavaScript and I'm <i>convinced</i> that if more people had this experience they would want the pipe operator in JavaScript.<p>Unfortunately it's hard to convey the advantages to those who haven't used it. Maybe it's because it's such a simple bit of syntatic-sugar?
const isRandNumOdd = Math.round(Math.random() * 100) |> % % 2 |> Boolean;<p>Excruciatingly contrived, but does this sort of arithmetic work in the Hack syntax? I'm genuinely curious, couldn't find any mention of modulo (or remainder) in the proposal.
To give credit where it's due. I came across the proposal through this video : <a href="https://www.youtube.com/watch?v=h1FvtIJ6ecE">https://www.youtube.com/watch?v=h1FvtIJ6ecE</a> by Theo the CEO of Ping.gg.
Well here’s a solution to a problem that didn’t really need any solving.<p>How about spending time on:<p>• Actors
• Real Immutable Structs<p>?<p>This is going to create more code unreadable, while it may be cool and clever to put a pipe in and call it a day, I imagine most production code will be a pile of nested pipe gibberish that any junior engineer or new hire would pull their hair at trying to piece together.<p>We want to create features, and solve business problems and that in turn also requires maintenance. This is just some clever macro wrapper around currying. While functional style is cool and all I don’t really see much value for most day to day workflows
I had a hard time rethinking decades of frontend projects where pipes would simplify code at least two times. However, in the backend please go ahead.<p>Having said that, can anybody provide an example with error handling per pipe?<p>You know servers are bitches :)
I don't think that it's a good idea to introduce a left-to-right flow into a language, which strictly assigns righthand values by general design. The text mentions a back-and-forth in reading, this introduces a back-and-forth intellectually.<p>(There's already a limited left to right capability, namely by chaining expressions using logical operators, especially when used outside of condition. It should be mentioned, however, that this is already confusing to some.)
This is cool but it always frustrates me to see these the TC39 focusing on these little improvements to the language instead of taking big bold steps that would have a much more significant impact.<p>Stuff like types, data binding, reactivity, etc. These would save so many kbs and CPU cycles if implemented natively. The world sorely needs that. God knows how much energy is wasted in sending and processing huge bundles of JS billions of times every day.
> In the State of JS 2020 survey, the fourth top answer to “What do you feel is currently missing from JavaScript?” was a pipe operator.<p>In 2021 (<a href="https://2021.stateofjs.com/en-US/opinions/" rel="nofollow">https://2021.stateofjs.com/en-US/opinions/</a>) it fell to 7th place.
I actually find the original easier to read than the piped example given for this one:<p>Original<p><pre><code> jQuery.merge( this, jQuery.parseHTML(
match[ 1 ],
context && context.nodeType ? context.ownerDocument || context : document,
true
) );
</code></pre>
-----------------------------------------<p>With pipes<p><pre><code> context
|> (% && %.nodeType ? %.ownerDocument || % : document)
|> jQuery.parseHTML(match[1], %, true)
|> jQuery.merge(%);
</code></pre>
I think F# pipes are ideal in more complex cases, the % can add unnecessary complexity when reading code. Alas, it looks like we're not going to get that.
I played with coconut recently (fp layer on top of python) and the `|>` syntax felt very annoying compared to `.` (even though it felt ok in Ocaml..)<p>But in an OO underlying language it's probably impossible to reuse it.
This makes me nervous.<p>In general, I think adding features like this to a mature language is a misstep because it increases the cognitive load of "things you have to know to read other people's code." And that's <i>strictly</i> increases... Since changes like this can't remove previous approaches (for backwards compatibility reasons), we'll now have <i>three</i> syntaxes for function calls? Yuck.<p>Left unchecked, this predilection eventually leaves you with languages like C++: a language where you <i>can</i> write good, safe code if you stick to modern methods, but <i>good luck</i> learning what "modern methods" are or finding tutorial books that don't teach you any of the bad-old approaches or, most importantly, working with other people's C++ code that still has `setjjmp` and `longjmp` in it because the language allows for it, therefore <i>someone used it somewhere.</i><p>(oblig: <a href="https://xkcd.com/927/" rel="nofollow">https://xkcd.com/927/</a>)
The syntax is simply disgusting, some examples look worse with pipes than without them.<p>I understand it is stage 2 now but can we do something about it? I'm seeing that most comments here and in other places are critical, why should we get this stuff in JS because of a vocal minority that is trying to push it on everyone else?<p>If we keep adding the fifth thing asked on state of JS year after year we will end with some frankenstein weirdness... JS was getting better and better, let's not make it worse.
This proposal has been languishing for years and years. I'm not sure when it last made progress, but I remember waiting for it with anticipation around 2018
Seems to me that someone "stole" it from F#. F# probably also take this idea from some unknown (to me) language. Here a similar idea in C++ <a href="https://www.fluentcpp.com/2019/10/18/piping-to-and-from-a-stream/" rel="nofollow">https://www.fluentcpp.com/2019/10/18/piping-to-and-from-a-st...</a><p>This is generally a weakness of text based programming languages, that you cannot easily express graph flows like:<p><pre><code> / B1 \
A>-| | -> C
\ B2 /
</code></pre>
|> operator only solves the problem for non-branched flows like A -> B -> C making them more readable by removing nested calls.<p>In theory you can create something similar using JS OOP by attaching e.g. map/use(func) methods to every prototype:<p><pre><code> function use(func) { func(this); return this; }
function map(func) { return func(this); }
</code></pre>
and then:<p><pre><code> (1).map(n => n+1)
.use(console.log)
.map(n => "n = " + n)
.use(n => console.log("str = $n"));
</code></pre>
Introducing a new operator instead of a library is a huge effort. I don't think this proposal will succeed, especially that current custom operator support in JS is nil.
<p><pre><code> three(two(one(value)))</code></pre>
.<p><pre><code> value |> one() |> two() |> three()</code></pre>
.<p><pre><code> a=one(value);
a=two(a);
three(a);</code></pre>
.<p><pre><code> pipe(value, one, two, three)
</code></pre>
all seem fine but nr 2, where does the return value from three() end up?
I don't see how syntactic sugar makes any sense for javascript. Breaking compatibility is so severe because every browser needs to catch up, yet it doesn't actually enable anything that couldn't be done before.
This feels like code golfing. To messy to explain to newbies. Just extra friction.<p>You have to explain how the syntax works, the environment it works in, and the error handling. What the hell.
This language is a garbage pile, a literal scrap heap. Look over there, that's the kitchen sink. For fucks sake we added the absolute aesthetic disaster that is CLASSES to the thing. The strength of this language is that we throw in everything and the kitchen sink. I've had one wish for the last 5 years and it's for this god damn operator to land in the language. Please just throw it in there like everything else, it will make my life so much more pleasant. If you don't like it you can just ignore it studiously just like I do with classes.
They should have stuck with the F# proposal. The hack proposal just takes one more giant step toward turning JS into Perl.<p>Hack proposal<p><pre><code> value |> foo(%) for unary function calls,
value |> foo(1, %) for n-ary function calls,
value |> %.foo() for method calls,
value |> % + 1 for arithmetic,
value |> [%, 0] for array literals,
value |> {foo: %} for object literals,
value |> `${%}` for template literals,
value |> new Foo(%) for constructing objects,
value |> await % for awaiting promises,
value |> (yield %) for yielding generator values,
value |> import(%) for calling function-like keywords,
</code></pre>
F# proposal<p><pre><code> value |> x=> x.foo() for method calls,
value |> x=> x + 1 for arithmetic,
value |> x=> [x, 0] for array literals,
value |> x=> ({foo: x}) for object literals,
value |> x=> `${x}` for template literals,
value |> x=> new Foo(x) for constructing objects,
value |> x=> import(x) for calling function-like keywords,
</code></pre>
F# proposal would make `await` and `yield` into special syntax cases or not allowed.<p>I'd rather do await/yield the old fashioned way (or slightly complicate the already complex JS syntax rules) than add the weird extra syntax. Arrow functions are elegant and already well-known and well-understood.