F#/ OCaml:如何避免重复模式匹配?

tts*_*ras 4 f# ocaml pattern-matching

看看这个F#/ OCaml代码:

type AllPossible =
    | A of int
    | B of int*int
    | ...
    | Z of ...

let foo x =
    ....
    match x with
    | A(value) | B(value,_) ->                   (* LINE 1 *)
        (* do something with the first (or only, in the case of A) value *)
        ...
        (* now do something that is different in the case of B *)
        let possibleData = 
            match x with
            | A(a) -> bar1(a)
            | B(a,b) -> bar2(a+b)
            | _ -> raise Exception    (* the problem - read below *)
        (* work with possibleData *)
    ...
    | Z -> ...
Run Code Online (Sandbox Code Playgroud)

那么问题是什么?在函数foo中,我们将模式匹配到一个很大的类型列表.一些类型共享功能 - 例如,他们有共同的工作要做,所以我们在上面的第1行中使用"| A | B - >".我们读取唯一的整数(在A的情况下),或第一个整数(在B的情况下)并使用它做一些事情.

接下来,我们想做一些完全不同的事情,这取决于我们是在A还是B上工作(即调用bar1或bar2).我们现在必须再次进行模式匹配,这就是问题所在:在这个嵌套模式匹配中,除非我们添加'catchAll'规则(即'_'),否则编译器会抱怨我们缺少案例 - 即它没有考虑到在这里只能发生A和B的帐户.

但是如果我们添加catchAll规则,那么我们就有一个更糟糕的问题:如果在某些时候我们在LINE1列表中添加更多类型(即在行'| A | B - >'...那么编译器将不会帮助我们进行嵌套匹配 - '_'将捕获它们,并且将在RUNTIME中检测到错误.模式匹配的最重要的一个功能 - 即在编译时检测到这样的错误 - 将丢失.

是否有更好的方法来编写这种代码,而不必在A和B的两个单独规则中重复A和B之间共享的任何工作?(或者将A和B共同工作放在一个仅为A和B之间的"本地代码共享"目的而创建的功能中?)

编辑:请注意,有人可能会说F#编译器的行为在这种情况下是错误的 - 它应该能够检测到嵌套匹配中不需要匹配A和B.

ygr*_*rek 5

如果数据类型是一成不变的 - 我也更喜欢本地函数.

否则,在OCaml中,您还可以享受开放(也称为多态)变体:

type t = [`A | `B | `C]
let f = function
| (`A | `B as x) ->
  let s = match x with `A -> "a" | `B -> "b" in
  print_endline s
| `C -> print_endline "ugh"
Run Code Online (Sandbox Code Playgroud)

  • 我认为这是我能得到的最接近答案:F#需要OCaml的多态变体 - 我希望MS的人都在听. (2认同)