有没有办法进行递归调用但具有不同的类型参数?
这是我认为应该编译的一个例子,但不是。
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在测试中也必须是同一类型。这是一个错误吗?
答案 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
中的'b
和let 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认为x
和y
必须具有相同的类型,并且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 y
和x
传递给y
。
有趣的部分是OCaml遇到test
时。您正在尝试将| Some false -> test None (swap f) y x
和y
(注意订单)传递给x
。为了让test
有效,test
和x
必须具有相同的类型,对吗?
基本上,上面是多态性递归的反面@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];;