Rubyパターンマッチを闇の力でアクティブにする
この記事はMisoca+弥生 Advent Calendar 2019の1日目です。
もう12月ですよ、12月!
記事の内容とはなんの関係もありませんが、デレステにM@GICが実装されましたね。
アニメを思い起こさせる最高のMVでした……
💎 Rubyのパターンマッチ
Ruby 2.7で、ついにパターンマッチが導入されますね。
今はRuby 2.7.0-preview3で試すことができます。正式にリリースされるのは、おそらく例年通り12月25日でしょうか。
パターンマッチ機能の使い方をざっくり書くと、case ... in ...
のような形でパターンマッチを行える機能です。
case { key: :value}
in { key: x }
p x # => :value
end
詳しい話はRubyKaigiでの発表スライドのほか、メドピアさんの「Ruby2.7の(実験的)新機能「パターンマッチ」で遊ぶ」がよくまとまっておりわかりやすいです。
🐫 F#のアクティブパターン
ところで、F#ではアクティブパターンという機能を使うことができます。
これは、パターンへの分解を、マッチ対象とは別に定義できるものです。
let (|Even|Odd|) input = if input % 2 = 0 then Even else Odd
let TestNumber input =
match input with
| Even -> printfn "%d is even" input
| Odd -> printfn "%d is odd" input
TestNumber 11 (* 11 is odd *)
TestNumber 32 (* 32 is even *)
上記のように、マッチ対象となる数値とは独立してEven
とOdd
というパターンを定義することができます。
さらに、パターンへの分解方法を変えることも可能です。
let (|RGB|) (col : System.Drawing.Color) =
( col.R, col.G, col.B )
let (|HSB|) (col : System.Drawing.Color) =
( col.GetHue(), col.GetSaturation(), col.GetBrightness() )
let printRGB (col: System.Drawing.Color) =
match col with
| RGB(r, g, b) -> printfn " Red: %d Green: %d Blue: %d" r g b
let printHSB (col: System.Drawing.Color) =
match col with
| HSB(h, s, b) -> printfn " Hue: %f Saturation: %f Brightness: %f" h s b
上記のように、Color
型に対してRGB値で取り出したりHSB値で取り出したりすることができます。
Rubyのパターンマッチでは、パターンへの分解はマッチ対象のdeconstruct
やdeconstruct_keys
を使うため、分解方法を切り替えるにはRefinementを使うのが現実的な落とし所です。
一般的な回答としてはRefinements使ってくださいですかね。その場合RGBかHSVかの使い分けはRefinementsのスコープが最小範囲になりますが。
— k_tsj (@k_tsj) November 19, 2019
deconstruct
などをパターン側ではなくマッチ対象側に持たせた理由は、辻本さんがn月刊ラムダノート Vol.1, No.3に書かれていました。設計判断の話や他言語との比較もいろいろ言及されていて興味深かったです。
……が、それでもF#っぽい書き方をしたい! と思ったのでそれっぽい実装を考えてみよう、というのが今回のテーマです。
ちなみに「闇の力」とタイトルに入っていますが、実装してみたらTracePointとかISeqとかを使わない形に落ち着いたので若干釣りタイトルになっています。
Continue reading