さて、今回はRuby 2.7 の新機能、Method reference operator .:
についてまとめていきます。
Ruby 2.7 の新機能に関しては、前回のNumbered parameters に続き今回で3本目のエントリーです🎉
なんだか、連載のような感じになってきてしまいました。
最初は1つのエントリーに全てまとめるつもりだったのに。。。
Method reference operator .:
TL;DR
Object#method
の代わりに、Method reference operator.:
を利用可能Dir["*/*.c"].map(&File.:basename)
といった書き方が可能- Numbered parameters は便利
使用例
# w/ Object#method m = 100.method(:to_s) #=> #<Method: Integer#to_s> m.call #=> "100" m = 100.method(:to_hash) #=> NameError (undefined method `to_hash' for class `Integer')
引数で指定したメソッドの名前をオブジェクト化した、Method オブジェクトを返します。
もし、その名前のメソッドが存在しない場合は、NameError
を返します。
メソッドをオブジェクト化したことにより、クロージャーとして利用することが可能になります。
これを、.:
を用いて下記のように書くことができるようになりました。
# w/ .: m = 100.:to_s #=> #<Method: Integer#to_s> m.call #=> "100" m = 100.:to_hash #=> NameError (undefined method `to_hash' for class `Integer')
挙動は Object#method
と同じです。
チケット
今回もチケットから導入の経緯、導入までの一連の流れを見ていきましょう。
今回の出発点は、下記のような処理をしたい時に、
- ブロックパラメターを利用する方法
w/o Object#method Dir["*/*.c"].map { |f| File.basename(f) }
Object#method
を用いる方法
w/ Object#method Dir["*/*.c"].map(&File.method(:basename))
どちらもあまりいけてないよね?というところのようです。
そこで、lambda で使われている->
を用いて、下記のような方法を用いるのはどうかと提案されています。
Dir["*/*.c"].map(&File->basename)
これを受けて、Matzさんは「アイディアは好きだけど、->
はちょっと。」と言った反応を示されておりました。
I like the idea of short hand notation for Object#method(), but I don't think -> is a good idea.
Matz.
チケットの中では.>
、<..>
、&>
、->>
、=>>
、+>
、$>
、:>
、\.
、Object[.method]
、:
や、
Elixir の|>
を用いたらどうかという代替案が出されていました。
また、メッセージセンドの::
を地上げし、再利用するという提案もありました。
ちなみに、Java のMethod reference には ::
が用いられているようです。
そんな中、Matzさん、
「候補の中で.:
がベスト、ついで :::
。...」とのこと。
...,
.:
looks best to me (followed by:::
)....Matz.
結果、.:
の形に収まったようです。
その他、Ruby がPerl に近づいていくことを危惧する意見や、これ以上記号をRuby に導入しないで欲しいといった意見などの Method reference operator の導入しネガティブな意見も散見されました。
コミットログ
今回もテストを中心に、読み解いていこうと思います。
基本的には、Object#method
と同様の挙動になるようテストが書かれています。
m = 1.:succ assert_equal(1.method(:succ), m) assert_equal(2, m.()) m = 1.:+ assert_equal(1.method(:+), m) assert_equal(42, m.(41)) m = 1.:-@ assert_equal(1.method(:-@), m) assert_equal(-1, m.()) o = Object.new def o.foo; 42; end m = o.method(:foo) assert_equal(m, o.:foo) def o.method(m); nil; end assert_equal(m, o.:foo) assert_nil(o.method(:foo))
通常のメソッド同様、改行にも対応してます。
assert_valid_syntax("a\n.:foo")
所感
Ruby 2.7 のMethod reference operator はなんとなく、Pattern matching とNumbered parameters の陰に身を潜めてしまっている感があります。 僕自身もPattern matching、Numbered parameters を先にまとめているので、何にも言えませんが。。。
w/o .: Dir["*/*.c"].map { |f| File.basename(f) } w/ Object#method Dir["*/*.c"].map(&File.method(:basename)) w/ .: Dir["*/*.c"].map(&File.:basename)
.:
の導入により、上記のように書くことが可能になりました。
こう比べてみるとスッキリしましたね。
そういえば、Numbered parameters を用いてもかけますね。
w/ Numbered parameters Dir["*/*.c"].map { File.basename(@1) }
少しだけ、Method reference operator .:
の方が記述量少ないのか🤔
にしても、Numbered parameters も便利ですね。
これまでそもそも Object#method
知らなかったので、
これを機にガシガシ Object#method
、Method reference operator .:
使っていきたいと思います。
では、今回はこの辺で。
最後まで読んで頂き、ありがとうございます🙇