SEAN_BLOG

プログラミング etc.

Ruby 2.7 の新機能(Pipeline operator |> 編) - チケットとコミットログを読んでみて

さて、今回はRuby 2.7 の新機能、Pipeline operator |> についてのエントリーです。 RubyKaigi 2019 のRuby Committers vs the World で取り上げられていたあれです。

6月13日にtrunk にmerge されたようですが、それ以降Twitter を中心に議論が盛り上がっていますね。 他の機能に対しても言えることですが、今後も機能が大きく変わる可能性がありそうです。

Pipeline operator |>

TL;DR

  • dot の代わりに、|> を利用することが可能。
  • |> はdot に比べて優先順位が低く、Range の括弧を省略可能。(e.g. 1..10 |> each { p @1 })
  • |> の後に他のoperator を続けることは不可。(e.g. 10 |>+ 10)

使用例

$ ruby -v
ruby 2.7.0dev (2019-06-16T05:46:28Z trunk 2fb1564c02) [x86_64-linux]
# w/o pipeline operator
(1..).take(10).map { @1 * 2 }
#=>[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

# w/ pipeline operator
1..
  |> take 10
  |> map { @1 * 2 }
#=>[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

基本的にはmethod 呼び出しのdot と同じ動きをします。 Pipeline operator |> は、dot に比べ優先順位が低いです。 そのため、上述のコードのようにRange の括弧を省略することが可能です。

Numbered parameters @1 については、こちらをご覧ください。

Ruby 2.7 の新機能(Numbered parameters 編) - チケットとコミットログを読んでみて - SEAN_BLOG

チケット

bugs.ruby-lang.org

全体を通りしてPipeline operator の追加に否定的な意見が多かったように見受けられました。 主な理由としてあげられていたのが、以下の2つ。

  • 関数型のPipe operator をRuby に持ち込むのは如何なものか。
  • ElixirやF#、Elm のPipe operator とRuby 2.7 の"Pipeline operator"の挙動は異なる。
# w/o pipe operator
foo(bar(baz(new_function(other_function()))))

# w/ pipe operator
other_function() |> new_function() |> baz() |> bar() |> foo()

上記は、Elixir のPipe operator のコード例です。

記述法はRuby 2.7 のPipeline operator と変わりません。

ただ、Elixir のPipe operator では、前のfunction の結果を次のfunction の第一引数としてとります*1。 つまり、上記の例ではother_function() の戻り値が、new_function() の引数として渡されることになります。 (baz() 以下も同様。)

一方で、Ruby 2.7 のPipeline operator では、前のmethod の戻り値が次のmethod の引数としては渡されません。

コミットログ

github.com

今回もテストコードを中心に見ていきます。

dot の代わりに、|> を用いていますね。

assert_equal("121", eval('x = 12 |> pow(2) |> to_s 11'))

dot を用いるとこんな感じですかね。

eval('x = 12.pow(2).to_s 11')

ただ、dot の場合、変数x12.pow(2).to_s 11 の戻り値である121 が代入されます。 それに対して、Pipeline operator ではx = 12 になっているようですね🤔

優先順位がことなるからですね。

assert_syntax_error('a|>-b', /unexpected '-'/)

こちらは、6月14日に追加されていたのですが、|> の後に別のoperator を用いることができなくなっています。 この点はdot と異なるようですね。

所感

全体的にPipeline operator の導入には否定的な意見が多かったように思います。 確かに、実際のユースケースを考えて見ると、dot ではなくPipe operator を利用するケースが思い付かない😇 果たしてこれは、僕の経験が無いからなのか、そもそも使い道がないからなのか。。。

といったところで、今回は失礼します。

最後まで読んで頂き、ありがとうございます🙇

参考

*1:F#、Elm の|>では、function の結果を次のfunction の引数の最後にappend する仕様のようです。