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.
如果数据类型是一成不变的 - 我也更喜欢本地函数.
否则,在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)