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

时间:2015-04-09 14:21:50

标签: 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 -> ...

我的问题是我应该在foo的签名中填写问号,以便我不需要(冗余)匹配语句?或者有一个击球手模式来做这个?

4 个答案:

答案 0 :(得分:7)

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

  1. 让函数foo简单地使用"有效负载"值构造函数A的值,并将类型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
    
  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
    
  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
    

答案 1 :(得分:5)

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

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 -> ...

但这里不需要约束。看起来你误解了类型约束的想法。一般来说,在OCaml类型约束中不能影响程序。具有和不具有类型约束的程序具有相同的行为。所以,在这里你根本不需要任何约束。您必须将类型注释视为程序员的辅助工具。

更新

我仍然不确定,我明白你想要什么,但是如果你想将你的变种分成子集,并保持这种分裂可以反驳,那么,你可以使用多态变体,就像Pascal建议的那样。 / p>

首先让我重新解释一下这些问题。假设我有类型:

  type t = A | B | C | D | E

我有一个模式匹配

  let f = function
    | A | B | C as x -> handle_abc x
    | D | E as x -> handle_de x

我如何向编译器证明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

因此,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

作为一个真实世界的例子,你可以看看BAP项目,其中多态变体用于表示instruction code。在这里,我们将所有代码分成不同的子组,如所有移动指令,所有分支指令等。之后我们可以直接pattern match对这些群组进行调查。

答案 2 :(得分:1)

您必须在问号中填写Foo签名的类型,然后在其中使用匹配语句。问号所在的位置表示类型。在某种程度上,它通过告诉它你想要什么样的确切类型来协助编译器,并且它将严格确保你在 a 输入上执行的操作属于匹配类型

匹配语句不是多余的,并且不会损害性能,因为它在OCaml中非常有效。但是我们有另一种方法如下。

或者如果您只有一个参数,则可以通过执行 function 代替匹配来保存一些输入。例如:

let foo (c:s) = match c with ....

我们可以做到

let foo  = function 
  | A -> ...
  | B -> ...

请注意,只有传入一个参数时,功能词才会起作用(如果你愿意的话,你可以将所有参数整理到列表中并传入)

这是另一个让我的观点得到解释的例子:

type number =  |Int  of int
               |Float of float

let to_int: (number -> int option) = function
| Int n -> Some n
| _ -> None 

(*this is the same as *)  (*also note that int option denotes return type*)

let to_int (input:number) : int option = 
 match input with 
 | Int n -> Some n 
 | _ -> None  

(*notice how the first one does not have a input as a parameter name*)
let n1:number = Int 1;;
let n2:number = Int 2;;

Example use: `to_int(n1);`

为了清楚起见,没有必要填写它,类型助手也帮助程序员,对我来说,在一些模糊的情况下帮助确保编译器知道我想要的东西。根据我的教授几个学期前的说法,明确提及它以控制类型是一个好习惯。

答案 3 :(得分:1)

incurs a runtime cost的一个解决方案是让变体包含元组而不是单个值。然后更容易捕获整个元组并将其发送到专门的函数:

type s =
  (* Note the extra parentheses! *)
  | Foo of (int * string)
  | Bar of (char * int * string)

let foo (i, s) = "foo"

let bar (c, i, s) = "bar"

let main input =
  match input with
  | Foo f -> foo f (* `f` is bound to a tuple of type `int * string` *)
  | Bar b -> bar b (* `b` is bound to a tuple of type `char * int * string` *)