matchのネストを減らす小ネタ
まれに判別共用体の値がどのケースか分かっていることがある。 一般論としてはケースが絞り込まれる場所であらかじめケースを剥がしておくほうがよい。
しかしどうしても剥がしたいことはある。 その場合はmatchを使い、その他のパターンを例外送出でごまかす。
type MyValue =
| Int of int
| String of string
let onInt (value: MyValue) =
match value with
| Int n ->
doSomething n
| _ -> failwith "unreachable"
この状況で使える小ネタとして、以下のヘルパーを用意しておくと記述量が減る:
let inline private (|Unreachable|) (_: 'T) : 'U = failwith "unreachable"
let onInt (value: MyValue) =
let (Int n | Unreachable n) = value
doSomething n
ここで Unreachable
というアクティブパターンを定義している。
必ずマッチに成功するので、let
の左辺で使っても「パターンが網羅的でない」という警告が出ない。
実行時の挙動は前述の match
と同じ。
Appendix. サンプルコード
/// Diverging active pattern.
let inline private (|Unreachable|) (_: 'T) : 'U = failwith "unreachable"
type MyValue =
| Int of int
| String of string
let doSomething (n: int) = printfn "int -> %d" n
// ----------
// match
// ----------
printfn "use match:"
let onInt1 (value: MyValue) =
match value with
| Int n ->
doSomething n
| _ -> failwith "unreachable"
onInt1 (Int 1) //=> int -> 1
try
onInt1 (String "")
assert false
with _ -> printfn "string -> raised"
// ----------
// let
// ----------
printfn "use let:"
let onInt2 (value: MyValue) =
let (Int n | Unreachable n) = value
doSomething n
onInt2 (Int 2) //=> int -> 2
try
onInt2 (String "")
assert false
with _ -> printfn "string -> raised"