Project

General

Profile

Feature #13559

Change implementation of Feature #6721

Added by dunrix (Damon Unrix) over 2 years ago. Updated over 2 years ago.

Status:
Feedback
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:81127]

Description

Hi,

please reconsider implementation of feature request #6721 planned for Ruby 2.5.0 .

Instead of introducing new method Object#yield_self, just reuse existing Object#itself by taking an optional block argument.

Find it much clearer and more logical solution, not superfluous polluting of API space.
Object#itself just returns target object, optional block would return alternative value with target object passed as block argument.

Prototyped sol. in Ruby:

class Object
  def itself
    block_given? ? yield(self) : self
  end
end

Not aware of any case, where it would break backward compatibility.


Related issues

Is duplicate of Ruby master - Feature #12760: Optional block argument for `itself`ClosedActions

History

Updated by matz (Yukihiro Matsumoto) over 2 years ago

  • Status changed from Open to Feedback

Why?

I don't understand the reason behind your statement 'cleaner and more logical'.

Return values from itself and yield_self are semantically different. Unifying them would make static analytics more difficult. Is there any reason over it?

Matz.

Updated by dunrix (Damon Unrix) over 2 years ago

I can see it just as an alternate way of expressing an identity function, not something foreign what would require standalone method. Just an optional modifier of received self. Thus more "logical", from my point of view I admit. Reusing already existing method instead of adding (yet another) new one find "cleaner".

Concerning static analysis objection, I'm not quite sure I understand the point. There are lot of methods in Ruby core, like in Enumerable, with block as an optional argument. What would be the difference to itself then ? Associativity precedence for the Ruby parser would be the same. Do you mean some code linters ?

Thank You.

Take care

Updated by zverok (Victor Shepelev) over 2 years ago

To be honest, the selected name for the method dozens of peoples wanted for 5 years looks really bad.

It is like instead of map Ruby'd had a method named like, I don't know, yield_each_and_return_result.

I always believed Ruby's method names are carefully selected to produce idiomatic, readable code.

So, I'd vote for itself too:

url.itself # return itself
url.itself { |u| open(u).read } # return "itself -> fetch from the network"

There are some other good options (listed, for ex., here), that are easy to read, like apply, as, chain, that will produce the code you could read "naturally".

yield_self is definitely the ugliest of them. It looks like ruby-core devs saying "we don't think we need this method, but OK, if you ask so frequently... take that!"

#4

Updated by nobu (Nobuyoshi Nakada) over 2 years ago

  • Is duplicate of Feature #12760: Optional block argument for `itself` added

Updated by nobu (Nobuyoshi Nakada) over 2 years ago

  • Description updated (diff)

zverok (Victor Shepelev) wrote:

url.itself # return itself
url.itself { |u| open(u).read } # return "itself -> fetch from the network"

It does not seem to return something other than url "itself".

There are some other good options (listed, for ex., here), that are easy to read, like apply, as, chain, that will produce the code you could read "naturally".

And let... but it is used by RSpec.
Or progn?

Updated by Eregon (Benoit Daloze) over 2 years ago

I agree #yield_self is less than ideal.

To start, it is quite long (10 letters including a underscore) for a very simple method.

In an ideal world, I think #itself with a block doing what #tap does today and #tap doing what #yield_self does would make sense.
But that cannot work with compatibility.

This blog post also made me realize it can be confusing as a method name:
https://bogdanvlviv.github.io/posts/ruby/new-method-kernel-yield_self-since-ruby-2_5_0.html

"#tap yields self to the block and then returns self."

That sounds exactly to what "yield_self" would do.

"#yield_self yields self to the block and then returns the result of the block execution."

#tap already "yields self" and the difference that the block value is returned is absent from the name yield_self.

I am unfortunately not sure what a good name would be.
Maybe #pipe ? (#| conflicts with Array#| so that's not good)

number.pipe { |x| x ** 2 }.next.pipe { |x| x ** 2 }

Fundamentally it works like #map on a single element, but that's already taken by Enumerable#map
and we need something that does not imply collections.

#as and #let seem the most descriptive but do not read well in a chain of method calls.

#itself seems a good compromise.
The current usage like ary.count(&:itself) to make a block that returns the element
and then expanding it to number.itself { |n| n*n } makes sense to me.
Instead of having &:itself be a simple block just returning the argument we can customize it by given #itself a block and return what we want
(which is what most blocks do, use the return value of the block instead of ignoring it).

Updated by Eregon (Benoit Daloze) over 2 years ago

nobu (Nobuyoshi Nakada) wrote:

And let... but it is used by RSpec.

Is that actually a problem?

https://github.com/rspec/rspec-core/blob/f4dc5ef5c6bded2b1ec348e856352b60a3e072e9/lib/rspec/core/memoized_helpers.rb#L249-L251
defines it on the example group, so I think it does not clash unless you want to use it inside the ExampleGroup itself, which seems useless (self already refers to the ExampleGroup).

Updated by zverok (Victor Shepelev) over 2 years ago

url.itself { |u| open(u).read } # return "itself -> fetch from the network"

it does not seem to return something other than url "itself".

well, for ME it is mostly "itself, processed this way".
But I understand that "how I read this code naturally" (considering you are Japanese and I am Ukrainian) is not an easy question :)

Updated by rosenfeld (Rodrigo Rosenfeld Rosas) over 2 years ago

Maybe something like translate, mutate or modify. Or single_map. I also read yield_self as doing the same as tap.

Updated by dunrix (Damon Unrix) over 2 years ago

Eregon (Benoit Daloze) wrote:

In an ideal world, I think #itself with a block doing what #tap does today and #tap doing what #yield_self does would make sense.
But that cannot work with compatibility.

This, exactly.
Just another reminder how important is think decisions through, before acting..

nobu (Nobuyoshi Nakada) wrote:

url.itself { |u| open(u).read } # return "itself -> fetch from the network"
It does not seem to return something other than url "itself".

Yes, result of a block can be anything. I wouldn't expect same object when optional block is passed. Like it already does Object#tap, unfortunately. It is all about point of view.

Updated by shyouhei (Shyouhei Urabe) over 2 years ago

Well, I know everyone don't like the name yield_self but, it seems that's a separate issue than this one. Can we focus? I guess we need another 5 years to find an "ideal" name of it and that's too off topic to what is requested here.

Updated by MSP-Greg (Greg L) over 2 years ago

Name proposal - cede

url.cede { |u| open(u).read } # return "itself -> fetch from the network"

Also available in: Atom PDF