7 ms·
ES7 Proposal: The Pipeline Operator
- k__ 11y agoI liked this feature in LiveScript rather much. Especially that it worked in both directions. my-list = filter <| sort <| get-data
- 1971genocide 11y agoDoesn't LiveScript already have it ?
- chc 11y agoAs noted in the OP, yes, as do F#, Elixir, Elm and Julia.
- dustingetz 11y agoCan we just put more focus on sweet.js so we can do this kind of in a library with standard build tooling? http://sweetjs.org/ http://sweetjs.org/ There's a lot more operators than |> that you would want. Clojure commonly uses ->, ->>, as->, etc. It should be done in a library if it is done at all. Also, going full-on functional in javascript is going to end badly, javascript simply wasn't designed for it. If you go down this path in real projects, sooner or later you inevitably end up at clojurescript or whatever other real FP language suits.
- wycats 11y agoCan you elaborate on the rough seams between JavaScript and "full on functional"? I'm not a believer in the FP vs. OO religion, and have historically been skeptical of the bind operator. That said, I think that both composition via objects and composition via functions have their place. The pipeline operator attempts to make composition via functions more readable (left-to-right instead of right-to-left) and unless you think composition via functions is a bad idea in JS in all cases, I'm not sure what "full-on functional" has to do with it.
- dustingetz 11y agojs objects are mutable; utliliy libraries like underscore, lodash, jquery, do not provide purity guarantees, they export impure functions that mutate objects (e.g. https://lodash.com/docs#merge https://lodash.com/docs#merge). When we start leaning heavily on function composition to build our abstractions, we simply must have purity guarantees for everyday utilities or everything falls apart. So to go "full-on functional" in javascript means we need to fork and rewrite the entire ecosystem. And if you're going to do that, there isn't much value in keeping javascript.
- lisivka 11y agoSo we need an (a) method to freeze object and it children, (b) to indicate that function will accept and/or return only read-only objects. Reserved "const" keyword is perfect for that.
- dustingetz 11y agoThat's unfortunately not what es6 const does - const is about re-assignment, not about immutability. (Though you can implement immutability in terms of non-re-assinment if it is const-all-the-way-down, which javascript objects aren't.) And you still need to re-write all the ecosystem to work with immutable objects.
- k__ 11y agoAre purity checks that hard to implement? I don't know much about this... Check that the function only writes on local variables and only reads closure variables and only calls other pure functions.
- gotchange 11y agoI think maybe he means that it is not idiomatic. It doesn't feel like JS with the proposed Ruby-like new bind syntax and now with this Unix-flavored pipeline operator, the language is taking on a new identity that some may not like.
- tragic 11y ago
- renekooi 11y agoI like this a lot. It's similar to the proposed function bind syntax[1], but a bit more friendly to not-`this`-related use: function bindMap(fn) { return this.map(fn) } [ 1, 2, 3 ]::bindMap(n => n + 1) // standalone use: bindMap.call(arr, fn) function pipelineMap(fn) { return arr => arr.map(fn) } [ 1, 2, 3 ] |> pipelineMap(n => n + 1) // standalone use: pipelineMap(fn)(arr) (Also nice that it works in a very similar way to decorators, although they won't be interchangeable in most cases, with decorators working on class constructors and property descriptors, instead of just any value.) Presumably, at most one of these proposals will make it in :) [1]: https://github.com/zenparsing/es-function-bind https://github.com/zenparsing/es-function-bind
- wycats 11y agoI agree that a "functional" operator that uses `this` as the first argument is weird. The main goal of all of these proposals is "left to right" composition when working with functions, and functions take parameters.
- RodgerTheGreat 11y agoIn concatenative languages, this style of composition is the norm, and it does indeed work well for many tasks: http://concatenative.org/wiki/view/Pipeline%20style http://concatenative.org/wiki/view/Pipeline%20style
- lisivka 11y agoIt is available in JS too: function double(str) { return str+', '+str; } function ucase(str) { return str.slice(0,1).toUpperCase()+ str.slice(1); } function exclamation(str) { return str+"!"; } var _="hello"; _ = double(_); _ = ucase(_); // _ = foo(_); _ = exclamation(_); console.log(_); // Hello, hello! function pipe() { var functions=[]; for(var i=0; i<arguments.length; i++) { functions[i]=arguments[i]; } return function pipe_call(arg) { for(var i=0; i<functions.length; i++) { arg=functions[i](arg); } return arg; } } var _="hello"; _ =pipe( double, ucase, // foo, exclamation )(_); console.log(_); // Hello, hello!
- donatj 11y agoI feel like that just confuses things for no good reason. It's no fewer characters than parens but provides as I see it less clarity.
- efdee 11y agoI think the clarity comes from the reading order. str |> method1 |> method2 |> method3 vs method3(method2(method1(str))).
- mbreese 11y agoThat's readable in the simple case, (arg1 is a string, returns a string). What about the more complex cases? Different method signatures, required extra arguments, returning objects or arrays? This will either a) break in those cases, or b) not be applicable. In which case, it's extra complexity for very little gain. Seriously, this just seems like syntax sugar that just isn't needed. I get that you might not think it's elegant, but is anyone seriously troubled or confused by method3(method2(method1(str)))? How would the pipeline syntax help here?
- whichdan 11y agoYep; same reason Lodash implements _.flow and _.compose. It also encourages you to write methods that take a single argument and return a single value, or partially apply those that don't.
- coldtea 11y agoHow it's "less clarity"? What happens is much easier to read if you know what the operator means. The only way the old way has more clarity is in the "we are already familiar with it" way. In any other way, this method trumps it.
- bratsche 11y agoIt's much less clear than the F# and Elixir versions of |> are, so if you've used those languages this feels confusing looking. It would be nice to introduce |> into Javascript while keeping it just as understandable as it is in every other language that already has it.
- pskocik 11y agoWhat's so painful about just doing `exclaim(capitalize(doubleSay("hello")));`? Why introduce needless syntactic sugar that'll only confuse people about how UNIX pipes work?
- thescriptkiddie 11y ago(because (there (is (such (a (thing (as (too (many parens)))))))))
- pskocik 11y agoI bet Lispers would beg to differ. :D Nested function do one thing on a chunk of data, then another, then another. *nix pipelines apply all the filters at once and control the scheduling and lifetime of the filters. Those are quite different concepts, and I think it's confusing to conflate the two. Plus every decent editor can handle paired parentheses.
- danneu 11y ago> I bet Lispers would beg to differ. :D Would they? I wrote and read threading macros all the time when I used Clojure. Turns out it cleans up code.
- efdee 11y agoI think you meant many_parens(too(as(thing(a(such(is(there(because)))))))).
- radicalbyte 11y agoTo understand the nesting you have to evaluate the functions in the opposite order than you're reading them: exclaim capitalize doubleSay Sure, some of us has spent 20 years doing this so it's easy, but you can't deny that this is far nicer to read: doubleSay capitalize exclaim I'm a big fan of Fluent Interfaces, so I use this construct fairly often: "hello".doubleSay().capitalize().exclaim()
- 11y ago
- wnfewnlkew 11y agoPlease no. Javascript's beauty lies in its versatile simplicity. Can we just leave these sorts of things to systems programming languages like C++? I came to javascript because of its lack of cruft, but if libraries start adopting this then I'll be forced to put it in my code as well. If that happens then I'll probably leave for nim or clojure, though that's not preferable.
- edgyswingset 11y agoCan't speak for nim, but Clojure has a near-exact feature which is used all over the place, the thread-first and thread-last macros. Example: (defn do-stuff [params-map] (-> (build-object params-map) (do-thing-with-object) (extract-thing) (format-thing))) I'm not sure why you'd choose Clojure to get away from things like |>.
- wnfewnlkew 11y agoClojure is a functional language, so it meshes well with the rest of the syntax, but in javascript's case it's just syntax bloat. It's going to be really jarring in the middle of regular imperative code, and it's not going to be too far removed from cout << "foo";
- danneu 11y agoWhether `|>` would mesh with your code depends on the code that's already there. Doesn't seem fair to shut down this proposal because you happen to write and work with imperative code, a style that Javascript and modern abstractions are moving away from. In fact, the Javascript hack to get a pipeline-like style is to make some opaque wrapper object that you can chain method calls onto like `_.chain(a).b().c().d().value()` in Lodash, when all you wanted was a chain of function application. `|>`, `<|`, composition operators, and friends would all be welcome complements to the Javascript code bases I've worked with in the last few years.
- takle 11y ago
- jordz 11y agoAs an F# user, I like this a lot, very similar although it can confuse people. It's very much a trait of functional programming and it's easy to read once you get the hang of it :)
- radicalbyte 11y agoThat's beautiful, the linear pipeline better matches your intuition about the computation than the nested functions. Can we have this in C# 7? :)
- danbruc 11y agoThis essentially exists - extension methods.
- radicalbyte 11y agoExtension methods only apply to instances and they must be declared explicitly. This syntax can be used anywhere as long as the types signatures of the functions are conducive to the chaining.
- danbruc 11y agoNot sure what you mean with applying only to instances, in which case couldn't you use an extension method? If you own the code making a function an extension method seems not a big deal. If you want to invoke a static function you don't own the code for then you could have a generic wrapper extension method which admittedly would probably look rather clumsy and nullify the desired gain in readability.
- mateuszf 11y agoBut you have to write them before using. It's not needed in case of the discussed feature.
- wycats 11y agoIf we're going to do this operator, I would prefer a regular arrow: import { sortBy, filter, map } from 'array-like'; let names = document.querySelectorAll('.entry') -> sortBy('title') -> filter(entry => entry.getAttribute('data-url') in whitelist) -> map(entry => entry.getAttribute('name'); I personally find this more readable than either the bind proposal (which looks too much like a property access, and not enough like a function call, to me) and the proposed pipeline operator, which doesn't have an immediate meaning to my eyes.
- wycats 11y agoThat said, the main counterargument to this proposal is that it's already possible to get "left to right" composition in userland without this operator, as underscore shows. The main counterargument to "do it like underscore" is that underscore still ends up being an object whose methods are added to a prototype, not imported. The benefit of the "pipeline" or "bind" approach is that you get left-to-right composition while still importing the names you're using.
- vruiz 11y ago> I personally find this more readable than ... the proposed pipeline operator, which doesn't have an immediate meaning to my eyes. Really? have you never seen unix pipes?
- wycats 11y agoI would find pipes to be very readable (but the single pipe operator is taken). The triangle isn't immediately an analogy to "unix pipes".
- tallerholler 11y agothat won't play nice with coffeescript!
- wycats 11y agoCoffeeScript should have thought of that ;)
- XorNot 11y agoI don't entirely understand where this gets me that promises don't.
- danneu 11y agoPromises are an abstraction for managing asynchronous operations. This proposal is about function application.
- Animats 11y agoThe single-argument case is elegant, and the multiple-argument case is awful. var newScore = add(7, validateScore( double(person.score) )) becomes var newScore = person.score |> double |> score => add(7, score) |> validateScore This is very Forth-like. Operands get pushed on the stack, and operators take from the stack and push results back. In Forth, there's no operator precedence and no parentheses; it's pure reverse Polish. Why not go all the way to pure RPN? 7 person score . double ValidateScore add newScore = See how it simplifies the syntax? Of course, you have to know how many arguments each function takes. Do we really want to go down this route? It's all syntax; it doesn't add any capability. As an extension to an existing language, it's likely to produce a mess. Mixing functional and imperative notations is a problem. LISP is all nested parentheses, and that bothered a lot of people. Forth eliminates all the parentheses, which bothered different people. Python was pure imperative, ("lambda" went in over major objections) and is gradually getting more functional features, but they don't fit the syntax well.
- simplify 11y agoIt's not just syntax; it actually opens new ways for API design. See abreza's comment: https://news.ycombinator.com/item?id=10686596 https://news.ycombinator.com/item?id=10686596
- Sonata 11y agoAfter having used this operator in Elixir and Elm, I would love to see it in JS. I know it seems like a superficial issue, but I think the left-to-right chaining flow is one of the main things people miss as they move from an OO style to a more functional one.
- gotchange 11y agoIt is not superficial or trivial at all. This proposal helps to ease the cognitive load required when reading or debugging code at least for the straightforward cases and makes writing code even more interesting and intuitive.
- pygy_ 11y ago@dang, was this manually penalized? Or is it a false positive to the flamewar detector? It looked like a fine submission. 52 points, an hour ago, and it lies on the third page...
- mateuspv 11y agoIf is to be more functional like e prefer add functions: compose and curry: 1 - // With pipe: var result = "hello" |> doubleSay |> capitalize |> exclaim; With compose: var greet = compose(exclaim, capitalize, doubleSay); greet("hello"); // or compose(exclaim, capitalize, doubleSay)("hello"); 2 - var person = { score: 25 }; // With pipe: var newScore = person.score |> double |> score => add(7, score) |> validateScore // With compose and curry: var newScore = compose(validateScore, curry(add(7), double); newScore(person.score); 3 - Mixin paradigms // With compose and curry : var newScore = Function.compose(validateScore, add.curry(7), double); newScore(person.score);
- deleted 11y ago[deleted]
- deleted 11y ago[deleted]
- deleted 11y ago[deleted]