-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
|
@@ -1379,6 +1379,13 @@ def test_numbered_parameter | ||
assert_syntax_error('@1', /outside block/) | assert_syntax_error('@1', /outside block/) | ||
end | end | ||
|
|
||
def test_pipeline_operator | |||
assert_valid_syntax('x |> y') | |||
x = nil | |||
assert_equal("121", eval('x = 12 |> pow(2) |> to_s(11)')) | |||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
amrabdelwahab
|
|||
assert_equal(12, x) | |||
end | |||
|
|||
private | private | ||
|
|
||
def not_label(x) @result = x; @not_label ||= nil end | def not_label(x) @result = x; @not_label ||= nil end | ||
|
30 comments
on commit f169043
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
awesome!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this, isn't the whole point of the pipeline operator that it passes the LHS as the arguments to the RHS? i.e 16 |> Math.sqrt #=> 4
Isn't the way it's implemented here almost exactly the same as .
(with a very minor difference on parenthesis use) ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To echo the above -- In my opinion, this feature is:
- Fairly pointless, as it adds more verbose alternate syntax to a solved problem.
- Invalid. This is not how pipeline operators are "supposed to" work. (I.e. what a pipeline operator means in other languages.)
Your implementation is:
foo |> bar 1, 2 == foo.bar(1,2)
But actually, what a pipeline operator is supposed to mean is:
foo |> bar 1, 2 == bar(foo, 1,2)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another -1 to this change - it makes zero sense.
Why is Ruby trying to be Elixir? Ruby is an OO language. The dot operator is designed for this purpose. The pipe operator is for functional programming.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π as well, it's just causes false expectations for fairly common use of that operator not just in JS, the whole ML world is using it the same way http://blog.shaynefletcher.org/2013/12/pipelining-with-operator-in-ocaml.html, https://fsharpforfunandprofit.com/posts/function-composition/#composition-vs-pipeline). IMO introducing this operator would make sense only when doing first some work on making functions more first-class citizens in the language (which I'm not sure how would be done with the current way of doings things).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And -1 from me. |>
as alternative for .
β what?
We have the same thing in JavaScript (experimental):
- https://github.com/tc39/proposal-pipeline-operator
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Pipeline_operator
And it works as expected by many people, not as in this commit. Please, remake or revert these changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another -1 to this change - it makes zero sense. Why is Ruby trying to be Elixir? Just donβt.
I think this would be far more acceptable if it was in fact similar to Elixir by providing functionality beyond aliasing .
. With a lot of data processing tasks, I often come across the desire to transform simple data in the style of Elixir -- but this feature doesn't allow that. It doesn't pass the return value as an argument to some other arbitrary function --> It requires that whatever you are calling next is still a method defined on the return value.
I think this feature as it stands is overwhelmingly confusing in 2019.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most Ruby developers won't read this discussion. It would be good to write it in the Redmine ticket.
I understand the first impression: the new syntax (|>
) just duplicates the existing one (.
). However, I noticed this might be similar to the relation between do ... end
and { ... }
. So, for example, we may use |>
for multi-line method chain and .
for single-line chain?
foo.bar.baz
foo
|> bar
|> baz
It somewhat makes sense because .
is easy to overlook:
foo.
bar.
baz
foo
.bar
.baz
But in principle, I'm against adding a symbol keyword. I'm yet unsure if it is good or not. I'd like to consider this feature for a while.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would be far more acceptable if it was in fact similar to Elixir by providing functionality beyond aliasing .. With a lot of data processing tasks, I often come across the desire to transform simple data in the style of Elixir -- but this feature doesn't allow that. It doesn't pass the return value as an argument to some other arbitrary function --> It requires that whatever you are calling next is still a method defined on the return value.
Exactly. Thatβs pretty much my point. This simply tries to make Ruby look like Elixir by implementing a functional operator familiar in Elixir. It doesnβt even operate the same way as Elixirβs pipe operator, as has been pointed out in many previous comments above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π on current implementation..
π to javascript version of pipeline operator enhanced with https://github.com/tc39/proposal-partial-application, which provide answer to whether return value should be pass in as first or last argument..
As for usage of pipeline operator in OOP language, I find myself writing private methods using functional style more and more.. especially useful when breaking down complex method into smaller "helper" methods..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would you add something called a pipe operator that's not actually a pipe operator?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a fan of this. It's:
- Mimicking Elixir for no good reason
- Not operating as a pipe operator should
- Trying to solve a solved problem
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the current implementation reflects Ruby's internal architecture. A method takes the receiver (or self
) as the implicit first argument. So, it is "a pipeline operator" from the viewpoint of some Ruby committers.
However, I believe this is not so natural nor practical from the viewpoint of most Ruby users.
Since Ruby is elegantly crafted, most Ruby users can forget about the implicit first argument (e.g. no need for my $self = shift;
). They can focus on writing OO codes on explicit arguments. Even when directly calling Method#call
, we are free from writing boilerplates. π
In this situation, it seems natural to expect the LHS result to be passed as the first explicit argument of RHS when using a pipeline operator |>
.
I want the new pipeline operator to be just as elegant and practical as the current overall Ruby design, hopefully from the viewpoint of Ruby users. β€οΈ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will write more on my thoughts later, but the example given of aliasing like do .. end
vs { ... }
is unconvincing as that already causes confusion for more Junior engineers.
In short I agree with most of the opinions presented here already that we should emulate Elixir and Javascript's style of pipeline operator.
As is we are adding sugar without substance, which I would advocate against as it does not increase the expressiveness of the language.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check out this operator-less pipe_operator proof of concept:
# before
JSON.parse(Net::HTTP.get(URI.parse(url)))
# after
url.pipe { URI.parse; Net::HTTP.get; JSON.parse }
"https://api.github.com/repos/ruby/ruby".pipe do
URI.parse
Net::HTTP.get
JSON.parse.fetch("stargazers_count")
yield_self { |n| "Ruby has #{n} stars" }
Kernel.puts
end
#=> Ruby has 15120 stars
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realize if I want to pass LHS value as the argument to other object's method, I can take the method from other Object and pass the method object to "then" method as a block.
16 |> then(&Math.:sqrt)
# => 4.0
require "net/http"
require "json"
"https://api.github.com/repos/ruby/ruby"
|> then(&URI.:parse)
|> then(&Net::HTTP.:get)
|> then(&JSON.:parse)
|> fetch("stargazers_count")
|> then(&"Ruby has %d stars".:%)
|> then(&self.:puts)
or use then with named parameters.
require "net/http"
require "json"
"https://api.github.com/repos/ruby/ruby"
|> then { URI.parse @1 }
|> then { Net::HTTP.get @1 }
|> then { JSON.parse @1 }
|> fetch("stargazers_count")
|> then { "Ruby has %d stars" % @1 }
|> then { puts @1 }
or refine #>
for shorthand
using Module.new {
revapply = Module.new {
def >(f)
f.call(self)
end
}
refine(Object) { include revapply }
refine(Comparable) { include revapply }
refine(Float) { include revapply }
refine(Integer) { include revapply }
}
16 |>> Math.:sqrt |>> self.:puts
# 4.0
require "net/http"
require "json"
"https://api.github.com/repos/ruby/ruby"
|>> URI.:parse
|>> Net::HTTP.:get
|>> JSON.:parse
|> fetch("stargazers_count")
|>> "Ruby has %d stars".:%
|>> self.:puts
# Ruby has 15854 stars
or convert argument to Proc by .:itself
then Proc#>>
then call it.
require 'net/http'
require 'uri'
require 'json'
"https://api.github.com/repos/ruby/ruby".:itself
|>>> URI.:parse
|>>> Net::HTTP.:get
|>>> JSON.:parse
|> call
|> fetch("stargazers_count")
|> then { puts @1 }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have captured my thoughts on this here: https://dev.to/baweaver/ruby-2-7-the-pipeline-operator-1b2d
I believe the alias functionality is actually a very good thing when combined with implementations from other languages to make what could potentially be a massive win for the Ruby language in terms of expressiveness.
As it exists now, I do not believe this is a good idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@baweaver This is fantastic:
def double(n)
n * 2
end
increment = -> n { n + 1 }
5 |> double |> double |> increment |> double
# => 42
I actually think a lot of people giving this feature thumbs up are assuming this is the usage, when in fact it is not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't the nature of the pipeline operator captured in then
blocks? If the pipe would alias the blocks then that'd make sense. In the current form, I don't see how the pipe is beneficial.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kubakrzempek Yes. I was also hoping then
(previously Object#yield_self
/ Object#as
) could support a true pipeline syntax like this, suggested here:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is quite literally NOT a pipeline operator, which makes this feature somewhat unnecessary and unexpectedly confusing.
@baweaver's suggestion of making |>
an alias to then
is great and fairly easy to implement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this some sort of belated April fools' prank? Could we please also introduce a this
keyword that points to a random object in memory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|>
aliased to then
looks better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So another way to inflate the "I committed XXX lines yesterday" count? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is contrary to the line "a programmer's best friend" in logo on the front page of https://www.ruby-lang.org/
-
aliasing '.' to '|>' is just plain useless and very few people will use it. this is wasted potential, because Elixir (+others) syntax is just plain gold and I would LOVE to have it in Ruby. this would ease up writing Service Objects in some cases or maybe even introduce new design patters e.g. "one object oriented service objects" (bad name sorry). some gems would even disappear, e.g. this one: https://rubygems.org/gems/solid_use_case/versions/2.1.1
-
what about programmers working both with Ruby and JS at the same time? and we are many, really. how can we be productive when we need to switch between meaning of the same operator all the time?
-
what about only-JS programmers that just have to read some of the backend side of their projects to understand something? or maybe change some simple code? i imagine the hell they are gonna be in when having to read code with this "new" operator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another nail in the coffin for ruby.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@awarmfastbear FYI this was reverted - 2ed68d0.
Isn't it just longer and less clear and less readable?