-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pipe Operator #4144
Pipe Operator #4144
Conversation
Wow, what a simple and clean patch! |
This looks nice to me. 👍 |
Pretty nice patch! Does it work with the pipe on the first line, and the call on the second line as well? (probably should "just work") |
@vendethiel yes it works f = (x,y) -> x + y
g = (x) -> x * 10
h = (x,y,z) -> [x,y,z]
10 |> f(1) |>
g
|> h(1,2) will be |
@hzamani Will I be able to pipe a value to an inlined, partially applied function? add = x -> y -> x + y
10 |> add(10) |
@seanstrom No, pipe is just a syntactic sugar for adding parameters to a function call, |
👍 Looking good! |
👍 one more reason to stick with coffeescript |
So what needs to be done to get this into CoffeeScript? 😄 |
@jashkenas 's approval |
Lodash already adresses this concern by |
Really looking forward to getting this really useful tool! |
Really nice, what does @jashkenas think? |
Yes, please! |
With functional programming styles on the rise this would be a very welcome addition to coffeescript. Some debaters in previous discussions have insisted OO style method chaining provides equally nice syntax. However there are certainly situations where it's not possible or wanted to have those methods on the objects worked on. A general trend can currently be seen where OO style is giving way for functional style so definitely time to rethink previous decisions. Adding pipes would enable nicer ways of dealing with more functional code designs. |
Having used this in Elixir, the pipe operator really encourages you to split things up into small functions and chain them together, it feels extremely satisfying and leads to some great code. Also leads to much simpler code compared to the OO-style chaining approach, since you're just returning the value instead of a scope that you need to hold onto in a closure (ie underscore). Would absolutely love to see this in Coffeescript. |
This would be a great addition. I don't agree with the idea of adding the statement as the first argument though. It would make the feature unusable for functional programming. |
I think |
Just wanted to add another idea: What if the pipe operator were a backslash ( Edit: I said the forward slash ^ before (mistake) I realize
Might be worth considering - might not be ~ |
@blitmap forward slash means division so don't think that's happening. Actually using pipe |
@dbackeus Ahh, I confused myself - I said forward slash but if you look in the examples I used the backslash 😄 Anyway, you're probably right - |
Backslash is already used by CoffeeScript (line continuations). |
@vendethiel Huh - TIL. I thought |
Been six months this is still open. I see there is a es7 proposal for this too. Would be awesome to get this in. |
It's a beautiful idea :) |
My example pointed out the specific utility of syntactic sugars that obviate the need for intermediate variable declaration. Given that your very previous question had to do with intermediate variable declaration, the example felt relevant |
@GeoffreyBooth I've found some code I believe could be improved by this operator, but before I spend my weekend completely rewriting
Thank you for your answers. |
@thepeoplesbourgeois to be honest, @jashkenas coming out against something is usually the death knell for a proposal. So I wouldn’t spend much if any more time on this. Personally I was trying to keep an open mind, but I feel like a lot of the arguments are just rehashes of “look how beautiful it is.” And sure, I get that beautiful syntax can lead to more understandable code, which leads to fewer bugs. So I’m open to being convinced. But I feel like this becomes less understandable for all but the simplest examples. Sure, this: result = 'hello'
|> doubleSay
|> capitalize
|> exclaim is better than the various ways it could be written in CoffeeScript today. But this: originalObj
.withSome()
.chainedMethods()
|> Array.prototype.yourMethodHere.call(yourMethodArgs) troubles me, because at a glance it looks like Obviously another issue is that this currently isn’t compiling correctly, which also means it doesn’t have enough tests. So there’s a lot of work to do on that front, aside from designing the syntax itself such that people aren’t confused by it and that smart people can reliably predict what the output will be. As we saw above with @lydell’s example, it currently also fails that benchmark. The reason I asked about real-world examples where you’d want to use this, is I wanted to see how often this would likely actually be used. I think the CoffeeScript codebase is a pretty good corpus, because there’s a great variety of code in there and many of the most arcane CoffeeScript features are used in once place or another. You’re welcome to choose something else if there’s another open-source project you know better. And I truly am open to considering examples where the pipe operator would significantly improve the legibility of real-world code as it stands today. But . . . I have a hunch that there wouldn’t be many clear-cut examples you could find where the pipe operator would be a dramatic improvement. How often do people really string together several functions, other than with chaining (which I consider superior to the pipe operator)? And even then, when it does happen I feel like most people do the intermediate-variable approach, like my But this goes back to my earlier question that also went unanswered: isn’t this promoting an anti-pattern? I feel like the current recommended way of dealing with stringing together functions is to take the object-oriented approach, chaining methods: So that’s where I am at the moment. This feels potentially helpful, but also very potentially confusing; it requires a lot more work, which I’m not sure it deserves; and I’m not sure how often it would ever get used, or if we should even be encouraging its use. |
Just a quick note on "bugs" in this PR, or this PR not "compiling correctly" – that's not the case. This PR is not buggy. It's just not doing what people expect in all cases. (That's actually the hardest part here: Defining what it should do in all cases, and being able to explain that to somebody else.) |
A business needs to be resistant to change because one customer said so. I think it raises a question about order of operations. Everyone knows But we have more operators than that, like 'or' foo or 2 |> double Do we get How priority is this bad boy, relative to other smooth operators? On Oct 13, 2016 10:35 PM, "Geoffrey Booth" [email protected] wrote:
|
I think it was kind of decided upon that this PR should be changed so that |
1 similar comment
I think it was kind of decided upon that this PR should be changed so that |
@lydell No, I don't think you want lowest of them all. Think what happens for |
Ah, good catch. So kinda lowest or lowest except assignment operators or something then. |
New syntax should have to pass a very high bar to be included, and this is falling quite short. I think that there's room for discussion about a pipeline-y feature, but it would:
If it can't do those things, then it probably isn't worth including — and this particular PR+talk doesn't seem like a great base to start from. Closing. |
I'm happy that you remain open to the pipe idea. Also you are obviously right that design for a new syntax needs to be really sound. Thanks for taking time on this issue.
I like the idea of cascading. Do you mean that the considered new operator should cover both cascading and piping? The functionality seems pretty different to me - maybe I'm narrow in my thinking. |
I see them as being very related. One is the object-oriented way of applying repeated operations to a value (either for side effects, or to mutate the value itself) div.show()
.animate(duration.slow)
.moveTo(coordinates)
.blink() And the other is the functional way to apply repeated operations to a value (either for side effects, or to mutate the value itself) div |> show
|> animate(duration.slow)
|> moveTo(coordinates)
|> blink Note that there's strangeness with the piping operator there because it's the first argument to If we're going to introduce new syntax, and it can't address both of these at once, the object-oriented style would be preferable to accomplish over the functional one. This is JavaScript, after all. |
Yes, as soon as more than one argument come into play it gets somewhat confusing. Do you think we can consider any kind of partial application syntax? It seems to me that together they would make more sense. |
Re. relation between cascading and piping. If we think of |
Just like LiveScript Piping? |
@yetone LiveScript has some awesome qualities and personally I would love to use it, but it's way more difficult than CoffeeScript to introduce in a workplace. It's less known and seems like learning curve for developers accustomed to OO and imperative style is too steep. Same arguments apply here as for the PureScript or Elm mentioned earlier by @igl . To all those disappointed by the rejection of this PR, here is a somewhat hackish way to avoid inside-out function application without big dependencies. It works by using single-element array as an applicative functor. Inspiration comes from Professor Frisbee's Mostly Adequate Guide to Functional Programming # Coerce number to a range and make sure it's a multiple of a different number:
[ result ] = [ number ]
.map coerce minimum, maximum
.map round multipleOf
.map adjust minimum, maximum, multipleOf
.map log "Coerced number"
# Provided that:
coerce = curry (minimum, maximum, number) ->
switch
when number < minimum
minimum
when number > maximum
maximum
else
number
round = curry (step, number) ->
switch
when not step?
number
when number % step is 0
number
when (number % step) < (step / 2)
step * (number // step)
else
step * (number // step) + step
adjust = curry (minimum, maximum, step, number) ->
switch
when number < minimum
number + step
when number > maximum
number - step
else
number
log = curry (label, x) ->
console.log label, x
return x By no means it's perfect as it requires instantiation of an array and only works with curried functions. I hope it's worth sharing anyway. Where there's a will, there's a way :) |
Every day I don't have a pipe operator |
I will say that it's odd but somewhat comforting that the CoffeeScript committee is showing restraint compared to TC39 around questions like this one. Having no answer to a problem is better than having a very wrong answer... Though, the decisions they're making while mad with power, and the blazing agility with which the Babel transpiler can adapt to those whims via its plugin architecture leads me to beg the question: Could I not simply plugin-architecture this functionality into my own deploy of the CS transpiler, if it were so vital to my workflow? |
@aurium the example at that link is amusing because f∘ g∘ f is always commutative. It seems deliberately confusing about the fact that |
The pipe is the language equivalent of "go into" x goes into h, which go into g I would probably not recommend using too many of these.. but I can see how it would be nice to have. |
… pipe operator. See jashkenas#4144.
Pipe operator
|>
adds its right hand expression as first argument to the left hand function invocation.Selecting first operand was a smart choice in Elixir as they don't have function currying. I think it's also a good choice for CoffeeScript, because of JS libraries common structure on accepting "subject" (i.e.
Object|Array|String|...
) as first arg.An answer to #1339 and #3600