RubyKaigi 2017に参加してきました。
今回は例年にも増して濃い感じのタイムスケジュールとなっていましたが、 Rubyへのパターンマッチ導入を目論んでいる身としてはその中でも@yotii23さんによるPattern Matching in Rubyが大変楽しかったです。Rubyの主要なカンファレンスにおいて、 Ruby本体にパターンマッチ用の構文をいれようという話が実装を伴って出てきたのは今回が初めてのはずで、それ自体意義深い事だったように思います。
提案されていた%pについては、Rubyistにとって馴染み深い文法というのは利点といえる半面、 裏を返すと馴染み深すぎてユーザに誤った印象を与えてしまいそう(オブジェクトに見えるので変数に代入したりすることができそうに思える)とも感じました。 この辺のバランスは難しいですね。
さて、発表に感化されて自分なりにプロトタイプを作ってみました。まだまだ荒削りですが、いずれFeatureチケット化までこぎ着けたいところです。
# caseバージョン case obj => =(pat, ...) [if|unless cond] ... => =(pat, ...) [if|unless cond] ... end # 代入バージョン =(pat, ...) [if|unless cond] = obj # パターン pat: var # 任意のオブジェクトにマッチし、varを束縛 | _ # 任意のオブジェクト | literal # ===メソッドの実行結果が真になるオブジェクト | Constant # 同上 | var_ # 同上(Elixirのピン演算子相当) | !pat # patにマッチしないか | pat && pat # 両方のpatにマッチするか | pat || pat # どちらかのpatにマッチするか | =(pat, ..., *var, pat, ..., id:, id: pat, ..., **var) # ScalaのExtractor相当(Rubyのメソッド仮引数とほぼ同等構文)
# caseバージョン class Object def deconstruct self end end class C def deconstruct [0, 1, [2, 1], x: 3, y: 4, z: 5] end end case C.new => =(0, *, =(b && Integer, _), x: 3, y:, **z) if b == 2 p b #=> 2 p y #=> 4 p z #=> {z: 5} end # 代入バージョン =(x:, =(y:)) = {x: 0, {y: 1}} p x #=> 0 p y #=> 1