Edited at

Ruby 2.5 の yield_self

More than 1 year has passed since last update.

Ruby 2.5 で Object#yield_self というメソッドが追加されます。

https://github.com/ruby/ruby/blob/v2_5_0_preview1/NEWS

https://bugs.ruby-lang.org/issues/6721

定義は簡単で、受け取ったブロックの引数に自身を渡して、そのブロックを評価した結果を返します。

class Object

def yield_self
yield(self)
end
end


簡単な例

"foo".yield_self{|s| s + s}

# => "foofoo"

tap に似ていますが、tap は自身を返すのに対し、yield_self はブロックを評価した結果を返します。

"foo".tap{|s| puts s + s}

foofoo
# => "foo"

今まで yield_self と同じことをするには tap の中で break する必要がありました。

"foo".tap{|s| break s + s}

# => "foofoo"


もっと実際的なケース

https://mlomnicki.com/yield-self-in-ruby-25/ を参考にしました。

GitHub の API にアクセスして、Rails リポジトリのスターの数を調べます。

require 'uri'

require 'net/http'
require 'json'

今までだったら、メソッドを入れ子にして書いたり

JSON.parse(Net::HTTP.get(URI.parse("https://api.github.com/repos/rails/rails"))).fetch("stargazers_count")

中間変数に入れたりしていたケースを

url = URI.parse("https://api.github.com/repos/rails/rails")

res = Net::HTTP.get(url)
data = JSON.parse(res)
data.fetch("stargazers_count")

yield_self を使うとメソッドチェーンで繋いで書いていくことができます。

"https://api.github.com/repos/rails/rails"

.yield_self { |s| URI.parse(s) }
.yield_self { |url| Net::HTTP.get(url) }
.yield_self { |res| JSON.parse(res) }
.fetch("stargazers_count")

ブロックなら中間変数と違って名前空間を汚すこともありません。

&記法でも書くことができますが、これは反ってわかりにくいかなと思います。

"https://api.github.com/repos/rails/rails"

.yield_self(&URI.method(:parse))
.yield_self(&Net::HTTP.method(:get))
.yield_self(&JSON.method(:parse))
.fetch("stargazers_count")


名前について

yield_self は長いなと思いました...。

元のissue を見ると、

image.png

みたいに言われたりしていて、他にも

apply, transform, alter, mutate, map, morph, cast

などの名前が候補として挙がっていましたが、結局は最初に提案された yield_self になったみたいですね。issue の議論は途中で途切れていて、最終的に決まった経緯はわかりませんでした。

簡単ですが、以上です!