我在SML中有这个表达式,需要找到它的最常见类型。当通过编译器运行时,我得到它在下面显示的内容。我怎样才能找到最常见的类型,不仅仅是这个函数,还有像教堂数字这样的其他函数“2”。
val one = fn f => (fn x => f x)
为什么是这种类型:
('a -> 'b) -> 'a -> 'b
答案 0 :(得分:2)
您所做的是,您应用名为 Hindley–Milner类型推断的流程。
一般原则包括三个步骤:
首先,我们将未确定类型(已编写'Z
,'Y
,'X
等)分配给变量和表达式。
three
已与int
类型绑定,则在val nine = three * three
中,我们知道three
的类型为int
。first
已与'a * 'b -> 'a
类型绑定,则在val firstOfFirst = fn quartet => first (first quartet)
中,我们为first
分配一个'Z * 'Y -> 'Z
,另一个类型为'X * 'W -> 'W
fn
。rec
或fn f => (f 1, f "one")
确定范围),那么该变量的所有出现都必须具有完全相同的类型 - 此时不允许多态。例如,在f
(最终给出类型错误)中,我们最初将所有出现的'Z
分配为单一类型int -> 'Y
。 (因为我们以后需要对string -> 'Y
和val one = fn f => (fn x => f x)
进行优化,所以会出现类型错误。这是因为标准ML不支持 first-class polymorphism 。)在您的示例f
中,我们可以指定'Z
类型x
,'Y
类型f
。
接下来,我们执行类型统一,其中我们识别必须匹配的不同子表达式的不同部分。例如,如果我们知道'Z -> real
的类型为i
且int
的类型为f i
,那么如果我们看到'Z
,我们就可以“统一” int
f
int -> real
并得出结论:f
的类型为x
。
在您的示例中,由于'Z
已应用于'Y -> ...
,我们可以将f
与'Y -> 'X
统一,并最终为('Y -> 'X) -> 'Y -> 'X
类型one
分配one
1}}。因此整个表达式的类型为('a -> 'b) -> 'a -> 'b
。
最后,我们执行类型泛化。一旦我们执行了所有可以执行的统一 - 一旦我们推断出关于类型的所有内容 - 我们就可以安全地用绑定类型变量替换未确定的类型。在您的情况下,这允许我们为{{1}}分配类型方案∀αβ。 (α→β)→α→β(对于任何和所有类型α和β,{{1}}具有类型(α→β)→α→β“)。在标准ML表示法中,我们不包括明确的“∀αβ”部分;我们只写{{1}}。
答案 1 :(得分:1)
我真的不明白你的问题,所以我只是猜测......
如果我在REPL中定义第一个教堂数字函数:
val c0 = fn f => fn x => x
val c1 = fn f => fn x => f x
val c2 = fn f => fn x => f (f x)
val c3 = fn f => fn x => f (f (f x))
val c4 = fn f => fn x => f (f (f (f x)))
...然后看看他们的类型:
val c0 = fn : 'a -> 'b -> 'b
val c1 = fn : ('a -> 'b) -> 'a -> 'b
val c2 = fn : ('a -> 'a) -> 'a -> 'a
val c3 = fn : ('a -> 'a) -> 'a -> 'a
val c4 = fn : ('a -> 'a) -> 'a -> 'a
...在第二个函数之后的类型('a - >'a) - > 'a - > 'a 出现了。这是您正在寻找的一般类型吗?
前两个函数的类型之所以不同,只是因为类型推断算法选择了最常规的类型。并且对于第一个函数'a - > 'b - > 'b 是一种更通用的类型,因为('a - >'a) - > 'a - > “一即可。但是你总是可以使用类型注释为编译器提供一个提示:
val c0 : ('a -> 'a) -> 'a -> 'a = fn f => fn x => x
val c1 : ('a -> 'a) -> 'a -> 'a = fn f => fn x => f x
...现在你的所有功能都应该有相同的类型。