Ruby

Ruby2.5で導入されるyield_selfについて

More than 1 year has passed since last update.

Object#yield_selfが導入されます。tapと違ってyieldの戻り値がそのままyield_selfの戻り値となっています。

class Object

def yield_self(*args)
yield(self, *args)
end
end

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

class Kernel

def tap
yield self
self
end
end

引用:Rubinius

これを見たときになんとなくパイプライン演算子ぽいなと思いました。Elixirでやるとこんな感じです。

IO.puts "I love Elixir"

|> String.upcase
|> String.reverse

# RIXILE EVOL I と表示されます

もし、Ruby2.5になったらこんな感じにかけます。

class Object

def yield_self(*args)
yield(self, *args)
end
end

puts "I love Ruby"
.yield_self(&:upcase)
.yield_self(&:reverse)

# YBUR EVOL I と表示されます。

Elixirはファンクションの第一引数に次々に値をセットしている一方で、Rubyはyieldの戻り値をチェーンしています。プログラミングの幅が広がればよいですが、いまのところベストプラクティスは思いつきません:sweat_smile:と思っていたのですが、頂いたコメントからヒントを得ることができました:tada:

tapではbreakを利用して戻り値に無理やりしていたことを自然に書くことが出来ます。文字列や整数の結果をチェーンするのを思いついたのですが、ステップごとに異なるクラスのインスタンスに変化していくときに活躍しそうです!(Elixirでの例とおんなじですね:smile:

require 'net/http'

class Object
def yield_self(*args)
yield(self, *args)
end
end

uri = 'https://www.libertyfish.co.jp/'
puts uri.yield_self(&URI.method(:parse))
.yield_self(&Net::HTTP.method(:get_response))
.code
# ステータスの200が表示される

蛇足ですが、tapと似たようなものだと思ってyield_selfに渡すブロックで破壊的メソッドを使った場合は思ってたのと違う!というのが起こりそうですね。

class Object

def yield_self(*args)
yield(self, *args)
end
end

puts "HELLO"
.yield_self{|greet| greet.upcase!}

# すでに大文字であれば、`upcase!`の戻り値は`nil`

puts "HELLO"
.yield_self{|greet| greet.upcase}

# すでに大文字であっても、"HELLO"

puts "HELLO"
.tap{|greet| greet.upcase!}

# すでに大文字であっても、"HELLO"

puts "HELLO"
.tap{|greet| greet.upcase}

# すでに大文字であっても、"HELLO"