使用相互递归键入推断

时间:2010-10-12 10:41:21

标签: recursion types ocaml type-inference

我一直在考虑类型推断如何在以下OCaml程序中起作用:

let rec f x = (g x) + 5
and g x = f (x + 5);;

当然,程序很无用(永远循环),但类型呢? OCaml说:

val f : int -> int = <fun>
val g : int -> int = <fun>

这完全是我的直觉,但类型推断算法如何知道这一点?

假设算法首先考虑“f”:它可以得到的唯一约束是“g”的返回类型必须是“int”,因此它自己的返回类型也是“int”。但它不能通过“f”的定义来推断其论证的类型。

另一方面,如果它首先考虑“g”,它可以看到它自己的参数的类型必须是“int”。但是之前没有考虑过“f”,就不能知道“g”的返回类型也是“int”。

它背后的魔力是什么?

2 个答案:

答案 0 :(得分:8)

  

假设算法首先考虑“f”:它可以得到的唯一约束是“g”的返回类型必须是“int”,因此它自己的返回类型也是“int”。但它不能通过“f”的定义来推断其论证的类型。

它无法将其推断为具体类型,但它可以推断出某些东西。即:f的参数类型必须与g的参数类型相同。因此,基本上在查看f之后,ocaml知道以下类型:

for some (to be determined) 'a:
f: 'a -> int
g: 'a -> int

查看g后,它知道'a必须是int

要更深入地了解类型推断算法的工作原理,您可以阅读有关Hindley-Milner type inferencethis blog post的维基百科文章,该文章似乎比维基百科文章更友好。

答案 1 :(得分:8)

这是我发生的事情的心理模型,可能与现实相匹配,也可能不符合现实。

let rec f x =

好的,此时我们知道f是一个带参数x的函数。因此我们有:

f: 'a -> 'b
x: 'a
某些'a'b

。下一个:

(g x)

好的,现在我们知道g是一个可以应用于x的函数,所以我们添加

g: 'a -> 'c

到我们的信息列表。继续...

(g x) + 5 

啊哈,g的返回类型必须是int,所以现在我们已经解决了'c=int。此时我们有:

f: 'a -> 'b
x: 'a
g: 'a -> int

继续......

and g x =

好的,这里有一个不同的x,让我们假设原始代码改为y,以使事情更加明显。也就是说,让我们将代码重写为

and g y = f (y + 5);; 

好的,我们在

and g y = 

现在我们的信息是:

f: 'a -> 'b
x: 'a
g: 'a -> int
y: 'a

因为yg的参数......我们继续前进:

f (y + 5);; 

这告诉我们y+5 yint类型,它解决了'a=int。由于这是g的返回值,我们已经知道它必须是int,这解决了'b=int。如果代码是

,这一步很多
and g y = 
    let t = y + 5 in
    let r = f t in
    f r;; 

然后第一行显示yint,因此求解'a,然后下一行会说r的类型为'b },然后最后一行是g的返回,它解决了'b=int