假设我有以下代码:
type s = A of a | B of b
let foo (a:?) =
let bar (input:s) = match input with
| A a -> foo input
| B b -> ...
Run Code Online (Sandbox Code Playgroud)
我的问题是我应该在foo的签名中填写问号,这样我就不需要(冗余)匹配语句了吗?或者有一个击球手模式来做到这一点?
如果你想避免重赛,我会看到3个解决方案:
让函数foo简单地获取值构造函数的"payload" A,并重新构造type的值s作为其输出(或与输出类型匹配的任何其他类型bar).
# type a;;
type a
# type b;;
type b
# module Ex1 = struct
type s = A of a | B of b
let foo (a:a) = A a
let bar (input:s) = match input with
| A a -> foo a
| B b -> (* ... *) input
end;;
module Ex1 :
sig
type s = A of a | B of b
val foo : a -> s
val bar : s -> s
end
Run Code Online (Sandbox Code Playgroud)使用多态变体:
# module Ex2 = struct
type s = [ `A of a | `B of b ]
let foo (`A a) = `A a
let bar (input:s) = match input with
| `A _ as a -> foo a
| `B b -> (* ... *) input
end;;
module Ex2 :
sig
type s = [ `A of a | `B of b ]
val foo : [< `A of 'a ] -> [> `A of 'a ]
val bar : s -> s
end
Run Code Online (Sandbox Code Playgroud)使用GADT:
# module Ex3 = struct
type _ s =
| A : a -> a s
| B : b -> b s
let foo (a: a s) : a s =
match a with
| A a -> A a
let bar: type x. x s -> x s = fun input ->
match input with
| A _ as a -> foo a
| B b -> (* ... *) input
end;;
module Ex3 :
sig
type _ s = A : a -> a s | B : b -> b s
val foo : a s -> a s
val bar : 'x s -> 'x s
end
Run Code Online (Sandbox Code Playgroud)从您的示例开始,解决方案很简单:
type s = A of a | B of b
let foo (a:a) =
let bar (input:s) = match input with
| A a -> foo a
| B b -> ...
Run Code Online (Sandbox Code Playgroud)
但这里不需要约束.看起来你误解了类型约束的想法.一般来说,在OCaml类型约束中不能影响程序.具有和不具有类型约束的程序具有相同的行为.所以,在这里你根本不需要任何约束.您必须将类型注释视为程序员的辅助工具.
我仍然不确定,我明白你想要什么,但是如果你想将你的变种分成子集,并保持这种分裂可以反驳,那么,你可以使用多态变体,就像Pascal建议的那样.
首先让我重新解释一下这些问题.假设我有类型:
type t = A | B | C | D | E
Run Code Online (Sandbox Code Playgroud)
我有一个模式匹配
let f = function
| A | B | C as x -> handle_abc x
| D | E as x -> handle_de x
Run Code Online (Sandbox Code Playgroud)
如何向编译器证明,handle_abc它只占用所有可能构造函数的子集,即A | B | C?
答案是,常规变种是不可能的.但是有多态变体是可能的:
type t = [`A | `B | `C | `D | `E]
let f = function
| `A | `B | `C as x -> handle_abc x
| `D | `E as -> handle_de x
Run Code Online (Sandbox Code Playgroud)
因此,handle_abc现在只需要对三个变体进行模式匹配,并且不需要任何冗余匹配.此外,您可以为一组构造函数指定名称,并在此名称上进行模式匹配:
type abc = [`A | `B | `C ]
type de = [`D | `E ]
type t = [ abc | de ]
let f = function
| #abc as x -> handle_abc x
| #de as -> handle_de x
Run Code Online (Sandbox Code Playgroud)
作为一个真实世界的例子,您可以看一下BAP项目,其中多态变体用于表示指令代码.在这里,我们将所有代码分成不同的子组,如所有移动指令,所有分支指令等.之后我们可以直接在组上进行模式匹配.