17 ms·
A proposal for adding a pipe operator to JavaScript
- ronenlh 5y agoI wrote about the pipe operator on ReScript (compiles to JavaScript), as well as its proposals in js here: https://link.medium.com/of6CK4nvvkb https://link.medium.com/of6CK4nvvkb
- da39a3ee 5y agoI don’t understand why javascript developers aren’t calling for comprehensions, like python/haskell (and standard mathematical set theory notation).
- JoelMcCracken 5y agoPersonally I find list compressions in Haskell overrated. I never use them.
- da39a3ee 5y agoInteresting data point, my impression was that they were used quite a bit in Haskell but I’m no expert. They are ubiquitous in Python, providing an extremely expressive means to construct lists, sets, maps, and generators. JS map and filter are exceedingly crude and unergonomic in comparison. Rust does it with a sophisticated iterator API.
- JoelMcCracken 5y agoThey seem to be used mostly in like beginner tutorials as examples of crazy cool stuff haskell can do. I actually just recently saw it in production code for the first time and was like "whaaaat is that?" It had been so long since I saw one that I didn't recognize it at first. The thing is, usually in real haskell code you aren't going to be using something that is roughly like set builder notation. Well at least, I haven't seen many scenarios where its needed. You're pulling data from someplace, manipulating it, etc, and then sending it somewhere else. That's not to say that I'm absolutely correct on this or whatever. But to me, overall I think Haskell syntax is far too complex and has too many special cases, and given list comprehensions are exactly equivalent to doing the same thing via the list monad, I'd prefer they be gone. I fear that someday we'll collectively need to replace GHC, and each and every weird edge case just means that its that much more of a giant task when that day comes.
- da39a3ee 5y ago> You're pulling data from someplace, manipulating it, etc, and then sending it somewhere else. Like this? stream = (manipulate(item) for item in producer_stream() if passes_filters(item)) consumer(stream)
- JoelMcCracken 5y agoof course, but its more normal to do: fmap manipulate $ filter passes_filters producer_stream And GHC has list fusion rules in this case (not sure if they exactly apply for list comprehensions; they probably do, but i'm not certain of that.) so IIRC the xyntax in haskell syntax your example would be smth like: [ manipulate x | passes_filters x, x <- producer_stream ] which is transformed to: do x <- producer_stream guard (passes_filters x) manipulate x Which I believe does not have the same run-time performance as my fmap/filter example; even if it does, I'm just saying I don't see it often, and its not idiomatic. Its different in python though because, well, python is more cursed.
- tome 5y agoI think you probably want `pure (manipulate x)` at the end of your do block.
- JoelMcCracken 5y agoYa could be. It depends upon the type of manipulate really. Thanks.
- da39a3ee 5y agoRight, thanks. Don't get me wrong, I like Haskell (though haven't got good at it yet; keep getting lost somewhere between fmap and applicatives/monads). However, I prefer the comprehension and `do` version over your `fmap` expression. The reason is that I think that filtering and transforming a stream is sufficiently common/important that a language should provide declarative syntax / a DSL for it. Whereas your `fmap` version makes too many implementation details explicit: all that should be needed is for the programmer to say "lazily filter and transform these values". I think it would be ideal for the programmer not to have to worry about `fmap` and `$`, and I think the expression should read more naturally than your fmap example. The Python and Haskell comprehension versions pass the "read naturally" test very well: "create a set/stream of manipulated items where the items pass the filter". The Haskell `fmap` version reads as "apply the manipulate function to a functor where, oh by the way the functor is a kind of list, and oh by the way use $ to separate arguments in this expression, but as I was saying do that but filter the list first". EDIT: I don't mean to imply that I know how Haskell should be written, I defer to you; the sophistication of Haskell's functor-related abstractions is such that I bet you're right that the special case syntax of a comprehension is kind of an annoying presence in the language (but that still leaves two ways to do it?). But, we were talking about javascript and there I think it makes a lot of sense to give the world a way to construct collections declaratively.
- k__ 5y agoThat's a pretty nice proposal. It's a bit sad that the discussion about the two different proposals slowed things down so much, but it's understandable. I'm looking forward to pipes!
- nobleach 5y agoThe decorators proposal has been stalled out for years! It's actually a bit scary as Typescript forged forward with the original proposal (Angular, NestJS apps have almost cemented these in many prod apps). So there stands to be a lot of breakage if the newer proposal is adopted. I'm sure there'll be codemods, but this is what happens...
- k__ 5y agoI never understood why JS needs decorators. It has pretty flexible function syntax already, so decorators wouldn't clean nearly as much code up as pipes would.
- nobleach 5y agoRight, and it definitely wasn't a comparison between the two proposals. I love OCaml/F#... and Elixir. So the pipes are cool with me. I've never tried the Hack variant however. As far as decorators as a "need". You're right. It's often as easy as `functionName(...args)(target)`. The decorators are definitely convenient for those coming from other languages that have annotations though.
- geoffeg 5y ago> However, nesting is difficult to read when it becomes deep: the flow of execution moves right to left, rather than the left-to-right reading of normal code. Laughs in LISP.
- didibus 5y ago(-> "a b c d" .toUpperCase (.replace "A" "X") (.split " ") first) "X"
- jml7c5 5y agoIs there a name for this sort of construct/macro in lisp? I'm interested in seeing more examples of it, assuming it's common.
- Spivak 5y agoPersonally I do find lisp code terribly difficult to read because of the inside out nature of it. For tricky Lisp I used to use a tool that would generate control flow graphs for me to make it easier to grok.
- gtf21 5y agoThis will be a great thing for Javascript. One thing I noticed, however, was that the main arguments for Hack-style pipes over F# pipes really highlight the difference between functional languages like Haskell and languages like JS: in Haskell one designs functions for composition (and more generally, one designs structures for combination), whereas the fact that F# pipes cause awkward contortions in JS is mostly related to the fact that functions aren't designed for composition. While I marginally prefer F# pipes, both will drastically improve the readability of JS code.
- lostmsu 5y agoI did a lot of of F#, and I disagree. While many functions have an argument that is clearly "primary", some just don't. I personally find placeholder to be a better option, especially if used with named arguments. Seeing your later comment about functional languages, I think they suffer from lack of overloading. E.g. F#'s fold2 would be much better as |> fold(a, %, func). Sometimes there's just no way around it.
- gtf21 5y agoI have never written anything in F# and I have no familiarity with the language, so I'm speaking more from my (IMO quite basic) familiarity with Haskell and Clojure, and most especially the former. In Haskell composition is just such a natural part of the design of programmes, and yes, functions often have many arguments. One just designed with composition in mind so this "piping" works really easily.
- Spivak 5y agoI think that’s my main issue with them. You shouldn’t need to write your code in a particular style to make a core language construct useful because of how often you deal with code that isn’t yours. Having to carry around a bunch of adapter functions kills any readability benefit.
- gtf21 5y agoI don't understand which "them" you mean -- are you talking about pipes or functional languages like Haskell?
- toastal 5y agoWhat's the purpose of a pipe operator without stressing the need for a composition operator? So often I see a function with the body return it's input parameter piped. That's just compose with extra steps.
- dnautics 5y agoWhat does composing into an arity-2 function entail? Do you automatically spread from an array? What if you need an array for your first parameter?
- guerrilla 5y agoI'm not sure but it sounds like you might be unfamiliar with currying [1]. 1. https://en.wikipedia.org/wiki/Currying https://en.wikipedia.org/wiki/Currying
- dnautics 5y agoI'm familiar with currying. In the real world, unless you have a certain type of parse transform, that's not the same as composition, due to lambda overheads.
- guerrilla 5y agoCurrying is the answer to your question, regardless of any possible real world overhead, which can be optimized out. This is a proposal to modify the language we're discussing, so I'm assuming they can do the latter as part of the proposal if that were necessary. Given f :: a -> b g :: (b, c) -> d h = uncurry ((curry g) . f) Then h :: (a, c) -> d
- jerf 5y agoIt isn't just "real world overhead", it's the ambiguities or pessimal behaviors you encounter if you try to bodge currying on to a language that wasn't designed for it. For currying, for instance, you need to know exactly when to execute the function and return the results, vs. when to return another curried function expecting more results. This is messy in the face of optional arguments, and in Javascript, all arguments are always optional. "But I can solve that with more syntax!" Perhaps so. But then you have the problem, what if you want to take advantage of optional arguments to add another argument to a widely used function? Do some of your invocations of the function that previously considered the function "done" now return functions expecting one more argument? Probably, unless your syntax forced explicit specifications of when to curry. Dynamic languages kinda have that sort of problem all over the place anyhow, but this would be a new manifestation for people to deal with. That's getting ugly and complicated. You're probably better off just using the lambdas the language already has rather than blundering your way through this mindfield. I don't think I can name a single case of a language solving this problem after the fact; by all means upgrade my knowledge by naming one that did (no sarcasm, I'd be interested in seeing it).
- aranchelk 5y agoI’m feeling grateful I get to use languages with user-defined operators (aka infix functions).
- halfmatthalfcat 5y agoScala?
- aranchelk 5y agoIn my case PureScript and Haskell.
- cageface 5y agoThe Hack pipe proposal reminds me a lot of Perl's special $_ variable.
- de_keyboard 5y agoF# pipes are much less of a departure from plain-old JavaScript than Hack-pipes.
- dvlsg 5y agoYeah. I dislike the hack pipes proposal because it introduces a bunch of syntax that only works mid pipe. F# style and a separate partial application proposal that could work anywhere in js (instead of just in a pipe) seems preferable to me. https://github.com/tc39/proposal-partial-application https://github.com/tc39/proposal-partial-application
- dbt00 5y agoI think this is obviously a useful tool and yet I worry that JS is going to end up in the C++ hole -- too much syntax for most people to remember, having to carve out practical subsets for readability.
- ainar-g 5y agoI'd argue, the too-much-syntax threshold has been passed back when destructuring assignments[1] got in. Now you can witness “beautiful” code like: const [,, { name }] = props; and: const { $ } = window; You can kind of reason about what's going on here, especially if you're a fan of one of the fancy languages with pattern matching, but if you're just a lowly backend developer in one of the “boring” languages, and you just need to fix this one thing in this one JS file, you're not going to have a good time. [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
- crate_barre 5y agoI’ll argue further suggesting Typescript has added to the too much syntax conundrum. Modern JS looks nothing like sensible syntax, and we are definitely in the realm of C++. But alas, no one ever listens in JS. They just add more and more shit until things are unrecognizable. People hate plain JS so much, that they’d rather mutilate into the language they prefer, and people are coming from every corner of programming with every type of preference (pun). One of these days I’m going create a barbarian horde of JavaScript developers and inundate another language, maybe Java, maybe Ruby or Python, and just vehemently insist that they must change their idiomatic style to that of Javascript. Let’s see how they like it. Anyone know why we have 30 imports at the top of each file like it’s Java? Where did that idea come from? Excessive types, huh? Since when? Have mercy on us, please. Edit: These were the welcome and necessary changes IMO: JS just really needed Classes as syntactic sugar for the odd way we replicated it with prototypes. That and the fat arrow (because binding ‘this’ was cumbersome), along with a basic import statement. Destructuring I can live with. Async/await was also necessary. We really didn’t need shit else. Small exception: I’ll make a veery small exception for a minimalist type system, or in other words, only 10% of typescript. I’ll take just the ‘Type’ and that’s all. Last note: Coffeescript did a better job synthesizing JS with ideas of other languages compared to ES6 honestly. Sometimes you have to think about how things mix/fit rather than ‘oh this language has it and JS doesn’t, so let’s add it’.
- foepys 5y agoDoes anybody more involved in this topic know how high the chance of this getting implemented is? It mentions that this has been rejected twice before, mainly for runtime optimization reasons, and that there is another proposal [1] that mentions this as an extension to its functionality. 1: https://github.com/tc39/proposal-pipeline-operator https://github.com/tc39/proposal-pipeline-operator
- taosx 5y agoIMO pipe operators wouldn't bring a big improvement from looking at the examples, some are becoming harder to read for me and I think temp vars would clean them more, in any case I would probably use them as they make code fun in some places. It would be nice to see more functional features, similar to async/await, template literals and lambdas. Some things that I think would be interesting are optional type hinting (native), exhaustive pattern matching.
- InfiniteRand 5y agoI realize they haven't committed to '%' as a final choice for the placeholder, but I'm wondering if there might be any ambiguous situations with the modulo operator. Perhaps the places where an operator can be used are sufficiently distinct from the places a placeholder might be used for that not to be a problem, but it bugs me a little that this is not addressed.
- yourad_io 5y agoI believe # is available as a symbol and I doesn't conflict with existing operators
- zodiakzz 5y agoAs someone who will be pulling their hair for the first few years it will have to be transpiled into obfuscated spaghetti code (and will be debugging/understanding it): Why do we need an operator just so people can write unreadable code. Instead of using... descriptively named variables? // A. Status quo (as per the proposal) console.log( chalk.dim( `$ ${Object.keys(envars) .map(envar => `${envar}=${envars[envar]}`) .join(' ')}`, 'node', args.join(' ') ) ); // With pipes (from the proposal) Object.keys(envars) .map(envar => `${envar}=${envars[envar]}`) .join(' ') |> `$ ${%}` |> chalk.dim(%, 'node', args.join(' ')) |> console.log(%); // A normal person who is able to write good code (me :P): const envVars = Object.keys(envars).map(envar => `${envar}=${envars[envar]}`).join(' '); const coloredText = chalk.dim('$ ' + envVars, 'node', args.join(' ')); console.log(coloredText); I'm not seeing it... can someone help me understand? Why are their so many proposals trying to force alien syntax into JS from "functional" languages?
- umvi 5y agoYeah but if you just write plain old readable code that doesn't use esoteric syntax sugar, then the problem is that almost anyone can understand it without having to google stuff.
- ainar-g 5y agoOP's link provides answers[1] to that question. And in my personal view, they aren't very convincing. The first answer, tedium, is unconvincing, because pretty much anyone with more than a couple of years of experience in software development in a team knows that ease of reading beats ease of writing. The second answer, unexpected mutation of temporary variables, seems to point to a deeper issue in JavaScript. That of const not quite being const. Unless you Object.freeze everything. The third answer, that temporary variables require sequences of statements instead of a single big expression, is more of a feature than a bug, in my personal opinion. Statements allow the reader to comprehend the algorithm one step at a time instead of trying to untangle one giant expression. To say nothing about the fact that a sequence of statements is way more debuggable. I'd say that JS is moving closer and closer to Perl in its write-only-ness. [1]: https://github.com/tc39/proposal-pipeline-operator#temporary-variables-are-often-tedious https://github.com/tc39/proposal-pipeline-operator#temporary...
- joshum97 5y agoIn response to people repeatedly advocating for F# + partial application over Hack, TC39 responds with what is essentially a threat: > To emphasize, it is likely that an attempt to switch from Hack pipes back to F# pipes will result in TC39 never agreeing to any pipes at all. PFA syntax is similarly facing an uphill battle in TC39 (see HISTORY.md). While I can respect differences in opinion, this feels heavy handed.
- azangru 5y agoWouldn't no pipes be better than hack pipes? Once a feature gets in, it will never get out. Whereas until it gets in, we can still hope for F# pipes :-)
- joshum97 5y agoWe've suffered under "bad parts" in JavaScript before, and the solution was not to use them, which works out fine for the most part. But I tend to agree, adding a functional language feature built in a way that conflicts with Ramda and nearly all existing FP libraries is no better than simply using R.pipe.
- zarzavat 5y agoI personally prefer F# pipes as it leaves the door open for partial application in the future, whereas Hack pipes closes that door because any partial application would be duplication. However, I feel like all the people using Ramda should just make their own language if they don't like the direction JS is going in. This is a "too many cooks spoil the broth" situation.
- tambourine_man 5y agoJavaScript used to be a tiny, quirky, but powerful language. Brendan Eich managed to get a lot of things right for such a tight scope, along with some very unfortunate decisions, of course. Now we have a language that just keeps on growing, the old quirkiness can't be swept away, and new ones keep adding up. I'm not sure it's a win.
- hajile 5y agoIf he'd been allowed to implement Scheme instead, none of these arguments would have ever been needed in the first place.
- tambourine_man 5y agoI think the familiar syntax was important in JavaScript's success.
- hajile 5y agoI think that was more a function of being the only browser language. The target of JS wasn't pro developers in those days anyway, so familiarity wasn't really a consideration. Most importantly, all the "new" features of JS would have existed for 25 years now. JS would have been fast in 1995 instead of waiting until 2008 to be usable. We've take years to add simple things like integers or arrays to JS that would have just been there. We're still debating things like threads which should also have been a solved problem by now. If you didn't like scheme, compiling to the language from another would be trivial too.
- mappu 5y agoBack in 2003-04, around the time of Gmail's launch and the inflection point of the massive explosion of DHTML evolving into Web 2.0, about 95% of internet traffic was using a browser that did support a certain second <script> language other than JS. It really could have gone differently.
- tambourine_man 5y ago
- Etheryte 5y ago> Temporary variables are often tedious I could not disagree more. Temporary variables as you perform transformations are great because they force you to verbalize what it is that you're actually doing. Descriptive naming is hard, but in contexts like this it's an actual godsend for anyone who isn't the person writing the initial code. I think the use cases are not convincing, I would much rather this be a Babel plugin than a core language feature.
- handrous 5y agoI hate that following assignment in the examples requires knowing precedence for the operator vs. the assignment operator. The = sign visually "points at" the first function call, but it's operating on the return value of the last function call, to which it has no spatial or visual connection.
- ravenstine 5y ago> Temporary variables as you perform transformations are great because they force you to verbalize what it is that you're actually doing. This is incredibly important because so many developers don't use ways to describe the intent of what they are writing at all. It's bad enough many believe the swill that "the code is the documentation", but to then add new ways to make code less self descriptive is... worrying.
- Zababa 5y agoSometimes things just don't have a good name. Focusing on a few well-named temporary variables that corresponds to logical steps usually yield clearer code than trying to name everything.
- ravenstine 5y agoThere's truth to that, but I also find that if I can't name something chances are that what I'm writing isn't as sensibly structured as it could be. I would rather solve these problems by creating new scopes than to effectively strip away names in favor of a chain of generic function calls.
- mattkrick 5y agoDo pipes make writing good code easier? Yes. Do they make writing bad code easier? Also yes. The older I get, the more I care about the latter. This is the first ES6+ proposal that I'm not excited about & I don't know how to voice that concern because it is so subjective.
- BigJono 5y agoI'm surprised this is the first one. How about the null coalescing operator that literally has zero use outside of encouraging an anti pattern in failing to disambiguate null and undefined at the earliest possible opportunity? How the fuck did that ever make it into the language? Some of the upcoming proposals are absolutely ridiculous too. A lot of these are ideas that would be fine for niche use cases, but the proposal itself has examples so stupid that they should be shot down immediately because the person doing the proposal doesn't know what the fuck they're doing (e.g. the ridiculous jsx example in the 'do' operator proposal where for some reason we now want to shoehorn a block of procedural code into a 50 line declarative expression instead of just using a basic one line ternary operator that has been in the language for years).
- thomas_moon 5y agoSeems to be trying to solve a problem that I would consider an anti pattern (attempting to 1 line everything). Someone else mentioned temporary vars and I think thats appropriate and equal if not better syntactically. This example: // Status quo return filter(obj, negate(cb(predicate)), context); // With pipes return cb(predicate) |> _.negate(%) |> _.filter(obj, %, context); Should in my opinion be written as: let result = cb(predicate) result = _.negate(result) result = _.filter(obj, result, context) return result The main benefit of this is its experience and almost language agnostic. You see a variable defined and it's run through a series of functions before being returned. What's the point of removing a few characters? edits: figure out how to format code, don't omit things
- zarzavat 5y agoWhat type is result? In TypeScript you'd have to give it an any type which means you lose type checking.
- joshum97 5y agoThis looks a lot better with the F# proposal: return cb(predicate) |> negate |> filter;
- chrisco255 5y agoYou're mutating result every step along the way and passing it into the functions as well, such that if it's an object or an array it could be mutated in non-transparent ways. The main benefit is to encourage a more functional, immutable data flow.
- thomas_moon 5y agoI get your point but I guess I fail to see how more obscure syntax at the time of composition helps someone understand something should be immutable. If I'm mutating inputs in a functional programming code base, syntax probably isn't the problem. edit: wasn't to isn't
- dragonwriter 5y agoYour suggested code and the “status quo” code are not equivalent, and the with-pipes version isn't the conversion of either of the other two to the proposed pipes syntax. Notably, on the first point, you proposed version drops two input variables from the other two versions, and on the second, the pipes version would correspond to the “status quo” version if the “_." prefixes were dropped. It looks like you've made your proposed version look cleaner by both omitting required elements from it and adding unnecessary (and code-breaking) elements to the with-pipes version.
- meetups323 5y agoAnyone else concerned that this whole thing is being justified by this survey[1], in which pipes were "the fourth most requested feature". They neglect to mention that the fifth most requested feature (<1% fewer votes than pipes) was "functions", making it pretty clear that we're probing the "people who have no clue what they're talking about" sector and we probably shouldn't be adding any new syntax to the language based on their input. Furthermore only 7% of respondents answered that question at all, so the real takeaway is: 0.7% of survey said "add pipes" and 93% said "please don't add anything it's fine as is just leave it alone we don't need to be the next Python". This isn't convincing data in the slightest. As for their actual example... why change it at all? I think any reasonably adept JS programmer can look at their representative "console.log" example and immediately tell whats going on. Heck, it's using the same semantics as every other C language, so really basically any programmer can look at it and immediately tell whats going on. Contrast to their "proposition" where suddenly everyone in the world (minus the Hack folks I guess) needs to take time to learn what the new weird looking symbols mean. 1: https://2020.stateofjs.com/en-US/opinions/#missing_from_js https://2020.stateofjs.com/en-US/opinions/#missing_from_js
- lostmsu 5y agoI read "functions" as a joke about JavaScript having weird scoping.
- justbaker 5y agoI can certainly agree on the first three, pipes I’m not all for. Just seems like another way to skin a cat that is less readable (to me at least). Functions being even on there is laughable. “I have 0 familiarity with the language but I must give my opinion!!!” And sanity… sanity is last on the list.
- aaomidi 5y agoA lot of the criticisms here show that many of you haven't worked with functional programming languages. All of which have pipe functions. This is fine, you can be an amazing programmer without ever touching functional programming. However, be a little bit more open to it, functional code is just another tool to use like every other tool. For some problems it's *the* solution. For others, it makes everything more complex. This doesn't make code less readable, you're just not used to reading code like this. After a week or two you realize how awesome this is. Piping has been a part of consoles forever now, what's wrong with bringing them into code?
- hn_throwaway_99 5y agoTotally agree. I commented elsewhere that I had someone of a similar reaction when Java introduced streams, in that the common paradigm is to just call successive operations on the stream you're processing. It took a while for my brain to get used to it, but once it did I found it so much easier to read, and much more efficient to write.
- bcrosby95 5y agoJava streams added no new syntax to the language. It's just a library. Unlike this proposal.
- ledauphin 5y agoI couldn't agree more. I feel like Hacker News is turning into a place where, especially for discussion of "popular" programming languages, the commenters seem to be largely those who don't have a solid foundation in the specific topic that's being discussed, but seem to believe they're experts because they've written a lot of code and have strong opinions on what code should look like. Conversations about more esoteric languages seem to attract the level of discourse HN is otherwise known for.
- 7sidedmarble 5y ago>Hacker News is turning into a place where, especially for discussion of "popular" programming languages, the commenters seem to be largely those who don't have a solid foundation in the specific topic that's being discussed, but seem to believe they're experts This is just HN in a nutshell. I've been wanting to write a blog post for a while about this topic. I have a pet theory that the HN 'feature' of not implementing direct comment reply push notifications (ostensibly a feature to dissuade flame wars) leads people on HN to make very strong statements they often can't defend. I think it's ultimately a net negative for the community. I get not wanting flame wars, but when you make strong statements on platforms that have the tools for better comment section engagement, you often have to defend your statements. I think this is a good thing.
- ledauphin 5y agoI guess I'll just throw myself to the wolves here by saying that I'm generally in favor of a pipe operator, I believe avoiding unnecessary temporary variables is a very valuable thing for the reasonability of code, and that while I'd prefer F# style pipes plus a clean partial application syntax/approach, I can certainly live with the Hack style pipes and agree that F# would be very unidiomatic in the absence of better partial application. Furthermore, I'm continually astounded by the naysayers who are opposed to language evolution as a general principle. I read these sort of objections all the time and all I can think is that if we took the "change almost nothing about our tools - think of the juniors!" approach advocated by these folks, we'd still be writing C and the industry would be an order of magnitude smaller than it is. Software development is a unique field of endeavor where it is remarkably easy to build and then use new tools, and while it does lead to lots of stressful change, it also leads to our discipline accomplishing more and more as the years pass. Thankfully, the silent majority seems to understand this just fine, which is why despite the tenor of the conversation around these (and other) parts, our tools do continue to get better and better as the years go by.
- handrous 5y agoThe more ways there are to write in a language, the harder it gets to read the code of someone else using a different style guide, or with different preferences, than you. This matters a whole lot in a language so completely dependent on 3rd party libraries as JS is. Not that I think this will hurt much, as the language and community fled that barn long ago. There are already 50 ways to do most things in JS, half of which will be present in your deps, and only 5 of which a typical active JS developer will happen to have stored in their brain at any given time.
- ledauphin 5y agoThis is certainly true. But this is also true of human languages, very few of which 'stop development' at the level of complexity of even the most complex programming language. I love a good lisp or ML, and all things being equal, less syntax is probably better than more. But flexibility is also a good thing, and the irony is that the proof is almost always in the pudding - if a language gets too complex (e.g. Perl, C++), it is almost always true that the community eventually establishes solid guidelines around which idioms to avoid (or when to avoid them). Sometimes there's pain, but rarely do languages die because of it. I think it's possible to have a reasonable debate about when to introduce new language features, but around here, in my opinion, the pendulum has swung very far toward the "don't move my cheese" side of things.
- nec4b 5y agoC# uses a third method instead of pipes or nesting, namely extension methods. https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods https://docs.microsoft.com/en-us/dotnet/csharp/programming-g...
- ModernMech 5y agoThis is interesting. Pipe operators are a big win in declarative languages because they allow you to implement a pipeline of operations, which goes against declarative semantics. Sequencing is difficult to express declaratively, thus pipe operators. But imperative languages are really good sequencing! The syntax for doing this in Javascript is ; Maybe pipes will give you some syntax wins, but it's not a huge win the way it is in declarative languages. I feel like what we are seeing in general is a lot of the good ideas of declarative languages making their way into imperative languages, sometimes without the best rationale. I'm not really seeing anything compelling in this proposal. It focuses a lot on legibility, but is that the biggest win here? And is it an unambiguous win? If you're going to go through all the trouble of adding this thing, it better be fore a really good reason with clear benefits that outweigh the costs. I worry that with some of the examples they give (probably the best cherry-picked ones they could find), I find the temporary vars version more understandable. Yeah there are fewer keystrokes, but I have not the slightest idea what this does: |> chalk.dim(%, 'node', args.join(' ')) But I know exactly what this does: const coloredConsoleText = chalk.dim(consoleText, 'node', args.join(' ')); Also, another reason this is not so great in Javascript is because the functions are impure. In pure functional languages you can chain functions together safely and with certainty because you know: 1) input arguments are immutable and will not be modified by the function 2) The function has no side effects. Therefore you can run these pipelines multiple times with the same input and you know what output you're going to get. If you have one or neither of these guarantees, then it's the wild west. What happens to the arguments when they go into that function? Are they mutated? Are they shared with another thread/process/worker? Are there any system calls in there? File IO? HTTP Requests? With this proposal, you have all these side channels that can be used and no guarantees about mutability or idempotency, or even whether or not the variables will have the same type after they are passed into a function. This just seems like the makings of a huge mess to me.
- zackham 5y agoIf your concern is that this is additional unneeded syntax that is just going to result in difficult to understand, overly terse code, my 2 cents is that a conversation among your team to discuss these cases and agree on some standards is a pretty straightforward solution. It's not like today's JS has the Python philosophy of "one obvious way to do it". I'll throw my hat in with those on the sidelines excited for the language we're stuck writing more of than we may want to, getting another piece of syntax that improves the ergonomics in a lot of little cases. Much like destructuring, much like the optional chaining and null coalescing. It will be nice to avoid the occasional temp variable that isn't useful for readability, or to rearrange expressions that read better applied than composed... but mostly I'm looking forward to how much nicer a pipe operator makes iteratively doing something in the console.
- danellis 5y agoIt seemed like a reasonable idea until I saw the syntax. Object.keys(envars) .map(envar => `${envar}=${envars[envar]}`) .join(' ') |> `$ ${%}` |> chalk.dim(%, 'node', args.join(' ')) |> console.log(%); Okay, from the other comments, I gather this is like Hack. Does any other language use a pipe operator for non-unary functions? Does the `%` syntax apply more generally? What is the type of `foo(%, 1, 2)`? One would expect it to be the same as `placeholder => foo(placeholder, 1, 2)`, but is it? For example, in Scala, if I have `foo(Int, Int, Int): Int`, then `foo(_, 1, 2)` has type `Int => Int`. That's true anywhere, but in the JavaScript proposal we seem to have a syntax that only applies in a special case.
- ovao 5y agoGiven the examples, the proposal seems overly-fixated on the ergonomics within JSX, which isn’t JavaScript. It seems more appropriate to just extend JSX so as not to introduce yet another layer of syntax to JavaScript (which is too complex as it is, in my opinion).
- hn_throwaway_99 5y agoSorry, this may be a dumb semi-related question, but on that proposal there is a link to "What do you feel is currently missing from JS?" from the 2020 State of Javascript survey, and just below the pipe operator is "functions". Does anyone know what that is referring to? Obviously JS has functions so I'm not sure what the context is here, and I couldn't find the original questionnaire that potentially had more details. https://2020.stateofjs.com/en-US/opinions/#missing_from_js https://2020.stateofjs.com/en-US/opinions/#missing_from_js
- ranguna 5y agoAs a comment above mentioned, it means actual functions which js current has, meaning the incredible minority of the people that voted on that question, don't know what's going on with js or are just trolling.
- didibus 5y agoInteresting, coming from Clojure/Script it bothers me that you need to repeat the pipe since I'm used to: (-> "a b c d" s/upper-case (s/replace "A" "X") (s/split " ") first) But I guess in JS you wouldn't know when to stop the piping. Also, in Clojure, you have -<> which is like a hybrid between Hack and F# pipes. Basically it defaults to piping into the first argument, but if you use <> anywhere, then where you've used it is where it will be piped instead, best of both worlds. (-<> "a b c" s/upper-case (str "result:" <>))
- fulafel 5y ago-<> is from a 3rd party library I guess? The joys of macros and extensible syntax :) The standard library has as-> , which doesn't have the implcit default. So you'd write (as-> "a b c" x (s/upper-case x) (str "result:" x))
- _ZeD_ 5y agoIt's ambiguos at best `foo(...)` normally means "call `foo`, but not with pipes function foo(str) { return `wrap(${foo})` } function bar(prefix) { return function(str) { return prefix + str; } } 'hello' |> foo; 'hello' |> foo($); 'hello' |> bar; 'hello' |> bar($); 'hello' |> bar('myprefix'); 'hello' |> bar('myprefix')($); What of those expression does the right thing?
- phplovesong 5y agoHave been following this since 2015-2016. The proposal got bikeshedded to destruction. What we wanted was a SIMPLE pipe opearator that pipes preferrably unary functions and composes them to a single expression. Then began the sheedding. Why does pipes haveto deal with async? Async is 99.9% IO realated, and now it has to be shoehorned into something thats by nature sync. On top of that now theres a magic variable so you could pipe functions accepting multiple params. He beauty of a pipeline is that its naive, and forces a certain code style, makin testing more easy, and code more elegant. Today im on the fence, as im not liking what the proposal has become.
- ranguna 5y agoI don't mind extending js, optional chaining and nulish coalescence are awesome, but this proposal is just alien! What's up with that implicit % argument?? Why are the authors so opposed to temporary variables? Having more explicit and correctly named variables makes the code so much easier to read vs slapping a % in the middle of the code. I would love to have pipes, but this is kinda ugly to me.
- deleted 5y ago[deleted]