OCaml递归调用不能有不同的类型参数?

时间:2014-05-13 19:31:46

标签: recursion compiler-errors polymorphism ocaml typechecking

有没有办法进行递归调用但具有不同的类型参数?

这是我认为应该编译的一个例子,但不是。

let swap (a, b) = (b, a)

module Test : sig
  val test : bool option ->
  ('a -> 'b -> 'c) * ('b -> 'a -> 'c)
  -> 'a -> 'b -> 'c
end = struct
  let rec test (b : bool option)
      (f : ('a -> 'b -> 'c) * ('b -> 'a -> 'c))
      (x : 'a) (y : 'b) : 'c =
    match b with
    | None -> (fst f) x y
    | Some true -> (test None f x y)
    | Some false -> (test None (swap f) y x)
end

编译器坚持认为即使没有理由,' a和&#b; b在测试中也必须是同一类型。这是一个错误吗?

4 个答案:

答案 0 :(得分:6)

这实际上是一个非常有趣的问题。

之前我不知道答案,但通过阅读我面前的两个答案,再加上一些研究,我将尝试解释并在下面回答。


基本上,你想要达到的是像这样的签名

val test : bool option -> ('a -> 'b -> 'c) * ('b -> 'a -> 'c) -> 'a -> 'b -> 'c

首先,我必须强调,试图强制参数为不同的多态类型,即使用'a, 'b, 'c等,不会必然迫使OCaml编译器认为参数必须具有不同的类型。

例如,如果你这样做

let f (x:'a) (y:'b) = x + y;;

似乎你强迫x和y成为不同的类型,但是在编译之后它会给出

  

val f:int - > int - > int =<有趣>

基本上,OCaml无论如何都会执行type inference,如果不是强制类型,则应用结论。

您可能认为'a中的'blet f (x:'a) (y:'b) = x + y;; 可能是x和y将具有不同的类型,也可能是相同的类型。所以强制这样的参数类型是没有意义的,对吗?


所以,让我们删除所有强制参数的类型,我们得到

let rec test b f x y =
    match b with
    | None -> (fst f) x y
    | Some true -> test None f x y
    | Some false -> test None (swap f) y x

OCaml将给出test的类型,如下所示:

  

val test:bool选项 - > ('a - >'a - >'b)*('a - >'a - >'b) - > 'a - >   'a - > 'b =<有趣>

所以,基本上,OCaml认为xy必须具有相同的类型,并且c不存在,因为要使用的OCaml的下一个可用类型标记是{{1} }。

为什么x和y必须具有相同的类型?

当OCaml符合'b时,它会认为let rec test b f x y的类型为x'a的类型为y

当OCaml符合'b'时,没问题,上述类型推断仍然有效,因为您再次将| Some true -> test None f x yx传递给y

有趣的部分是OCaml遇到test时。您正在尝试将| Some false -> test None (swap f) y xy(注意订单)传递给x。为了让test有效,testx必须具有相同的类型,对吗?


基本上,上面是多态性递归的反面@Jeffrey Scofield回答的问题。

y表示,函数可以有一些参数,其类型可以在递归期间更改,而不是保持不变。

默认情况下,OCaml当然只允许常量参数类型。


polymorphism recursion

那么如何解决呢?

您需要So how does rank 2 polymorphism work?类型rank 2 polymorphism。看看我的问题:In OCaml, what type definition is this: 'a. unit -> 'a

如果我们在类型定义中使用for_all'a.,则表示'a.

'a 'b.

以上是测试的新版本。

您为it is really for all types, real polymorphic types, and please, ocaml, do not narrow them down as long as it does not harm函数强制使用let rec test : 'a 'b. bool option -> ('a -> 'b -> 'c) * ('b -> 'a -> 'c) -> 'a -> 'b -> 'c = fun b f x y -> match b with | None -> (fst f) x y | Some true -> test None f x y | Some false -> test None (swap f) y x 类型,对'a 'b.test强制类型,ocaml会认为它们实际上都是多态的,因此参数{{1两个订单都可以接受和'a

答案 1 :(得分:4)

您要求进行多态递归,在一般情况下,类型推断是不可判定的:Wikipedia on Polymorphic Recursion。所以我不认为这是一个错误。

我认为有很多方法可以使用rank-2多态性获得你想要的东西。

答案 2 :(得分:1)

我认为这是一个错误,因为如果你创建一个新函数(与第一个函数相同),那么类型是正确的:

# let rec t b f x y = match b with
  | true -> (fst f) x y
  | false -> u true (swap f) y x
  and u b f x y = match b with
  | true -> (fst f) x y
  | false -> t true (swap f) y x;;
val t : bool -> ('a -> 'b -> 'c) * ('b -> 'a -> 'c) -> 'a -> 'b -> 'c = <fun>
val u : bool -> ('a -> 'b -> 'c) * ('b -> 'a -> 'c) -> 'a -> 'b -> 'c = <fun>

答案 3 :(得分:1)

使用OCaml 4.01.0:

module rec Test : sig 
  val test : bool option -> ('a -> 'b -> 'c) * ('b -> 'a -> 'c) -> 'a -> 'b -> 'c 
  end = struct
  let rec test b f x y =
    match b with 
       | None -> (fst f) x y
       | Some true -> (Test.test None f x y)
       | Some false -> (Test.test None (swap f) y x) 
end;;

请参阅Recursive modules部分。

作为测试用例的小例子:

 let add_float_int x y = x +. (float y);;
 let add_int_float x y = add_float_int y x;;
 let adds = add_float_int, add_int_float;;
 List.map (fun x -> Test.test x adds 10 10.) [None; Some true; Some false];;