SEAN_BLOG

プログラミング etc.

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

今回はRubyKaigi 2019 に参加して俄然モチベーションが上がったので、 NEWS for Ruby 2.7.0 の中から気になった点をいくつかピックアップしてまとめていきたいと思います。*1

パターンマッチング

使用例

def pattern_match(arg)
  case arg
  in [a]
    p a
  in { key: a }
    p a
  end
rescue NoMatchingPatternError => e
  p e
end

pattern_match([1])
#=>1
pattern_match({key: 'value'})
#=>"value"
pattern_match('string')
#=>#<NoMatchingPatternError: string>

挙動としてはこのように、パターンが一致すれば値をキャプチャすることができます。 また、全てのパターンと一致しない場合は NoMatchingPatternError の例外を投げます。

ちなみに、現状Experimental なので、パターンマッチングを実行すると、 下記のようなwarning が出ます。

pattern_match/sample.rb:2: warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!

チケット

bugs.ruby-lang.org

元々k-tsj/pattern-match というgem で開発されていたようです。 gem のリリースは2012年ということなので、遡ること7年ほど。

お恥ずかしながらこちらのgem の存在を初めて知りました。 正直、詳しいことはわかりませんが、README を見る限りRuby trunk のパターンマッチングとはシンタックス等大きく変わっているようです。

チケット内で下記の2点がデザイン・ポリシーとして挙げられています。

  • Keep compatibility
  • Be Ruby-ish

"compatibility" に関しては、RubyKaigi 2019 のなかでMatzさんを始め、 他のRuby comitter の方々も言及していたように記憶しています。

k-tsj/pattern-match の方では Object#match という形で、パターンマッチングが表現されていました。 しかし、match を導入すると、String#match や、Regexp#match 等の既存のコードを壊してしまう恐れがある。 そういった理由でmatch の利用は見送られ、case 文を拡張する形で実装が進められたようです。

"Be Ruby-ish" という点に関しては、Ruby の動的型づけ言語としての強みを活かすよう意識されたようです。

"Keep compatibility" と"Be Ruby-ish"。 言葉で言うのは簡単ですけど、実現するのは本当に大変そうです。

こういったRuby committer の方々の苦労に支えられているのですね。いつも本当にありがとうございます。 自分も少しずつRuby community に恩返ししていきたいです。

コミットログ

github.com

さて、ここからは実際のコミットログを見て少しだけ深掘りしていきます。 C言語が全く分かりませんので、今回はテストコードをヒントに紐解いていきます。

assert_block do
  case 0
  in a if a != 0
  else
    true
  end
end

このようにguard exp を置くこともできます。

assert_block do
  case 0
  in 0 => a
    a == 0
  end
end

as pattern を用いることで、このように変数に代入することも可能です。

assert_block do
  [0, 1].all? do |i|
    case i
    in 0 | 1
      true
    end
  end
end

| を用いて、or を表現することも可能です。

assert_block do
  a = /a/
  case 'abc'
  in ^a
    true
  end
end

^ を使うと、既に定義されている変数と比較することも可能のようです。 下記の通り、^ を利用するとa = 0 がパターンマッチに利用されています。

a = 0
case 1
in a
  p a
end
#=>1

a = 0
case 1
in ^a
  p a
end
#=>NoMatchingPatternError

所感

パターンマッチングは、API とかでJSON をparse するときに用いたら便利そうだなと。 強い方々がRuby 2.6 のASTxPattern matching とかよく耳にするので、そちらもやってみたいです。 まずは、AST を調べるところから始めたいと思います。

今回RubyKaigi 2019 で感化されて、初めてRubyredmine のチケットやコミットログとか見てみましたが面白かったです。

普段Ruby committer の方々がどういった意識でRuby の開発に取り組まれているのかを垣間見ることができました。

正直なところコミットログに関しては、C言語ができないと言う理由で距離を置いていました。 しかし、実際に見てみるとテストコードから基本的な挙動を理解することができました。

自分みたいなC言語できない人も安心です。

「テストコードはドキュメントの代わり」という言葉の意味をやっと理解し、テストコードの重要性を再認識しました。

とはいえ、C言語を習得してコミットログちゃんと理解できるようにしたいなぁ、、、今後の課題です。

参考

*1:まとめ始めたら長くなってしまったので、いくつかの記事に分けたいと思います。(気が向いたベースで書きます。)