OCaml:如何正确处理和类型?

dor*_*mon 4 ocaml

假设我有以下代码:

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的签名中填写问号,这样我就不需要(冗余)匹配语句了吗?或者有一个击球手模式来做到这一点?

did*_*erc 8

如果你想避免重赛,我会看到3个解决方案:

  1. 让函数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)
  2. 使用多态变体:

    # 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)
  3. 使用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)


ivg*_*ivg 6

从您的示例开始,解决方案很简单:

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项目,其中多态变体用于表示指令代码.在这里,我们将所有代码分成不同的子组,如所有移动指令,所有分支指令等.之后我们可以直接在组上进行模式匹配.