ブログ記事からの転載です。
と、いうのが bugs.ruby に来ていたので。
class Runner
def run
end
def stop
end
end
runner = Runner.new
case runner
in .run & .stop
:reachable
in .start & .stop
:unreachable
end
ほしい気持ちはわかるんだけど流石に上記の提案だと情報が欠落しすぎているのとそもそもパターンマッチでやるべきこと?と思ってしまい個人的にはいまいち。
なんかもっといい感じの構文だといいとは思うんですが…。
と、言うことで既存のパターンマッチで出来ないかやってみました。
using Module.new {
refine Object do
# パターンマッチ内部では #deconstruct_keys を暗黙的に呼び出してそれで判定を行っている
# そこで deconstruct_keys を経由してメソッド情報を取得することでパターンマッチで利用できるようにする
# { メソッド名: 値... } となるような Hash を返す
def deconstruct_keys(keys)
keys.select { |name| respond_to?(name) }.to_h { |key| [key, send(key)] }
end
end
}
def check(obj)
case obj
in { run: _, stop: _ }
:reachable
in { start: _, stop: _ }
:unreachable
else
:none
end
end
class Runner
def run
end
def stop
end
end
runner = Runner.new
p check(runner)
# => :reachable
class Runner2
def start
end
def stop
end
end
runner2 = Runner2.new
p check(runner2)
# => :reachable
Ruby のパターンマッチでは暗黙的に #deconstruct_keys
(や #deconstruct
)が呼び出され、その戻り値を参照してパターンマッチを評価します。
上記の実装では { メソッド名: 値... }
というような Hash をパターンマッチで使用できるようにすることで
case obj
in { run: _, stop: _ }
:reachable
in { start: _, stop: _ }
:unreachable
else
:none
end
というようなパターンマッチをかけるようにしています。
これならメソッドが定義されているかどうかを判定することも出来ますし『任意のメソッドの値』をキャプチャすることも出来ます。
# obj.run の値をキャプチャする
case obj
in { run: status, stop: _ }
p "status is #{status}"
:reachable
in { start: status, stop: _ }
p "status is #{status}"
:unreachable
else
:none
end
これ、かなり汎用性が高そうなので普通にほしい。ってか、すでに機能としてありそう。