Sml教堂数字类型推断

时间:2015-02-18 22:01:57

标签: recursion sml church-encoding

我在SML中有这个表达式,需要找到它的最常见类型。当通过编译器运行时,我得到它在下面显示的内容。我怎样才能找到最常见的类型,不仅仅是这个函数,还有像教堂数字这样的其他函数“2”。

val one = fn f => (fn x => f x)

为什么是这种类型:

('a -> 'b) -> 'a -> 'b

2 个答案:

答案 0 :(得分:2)

您所做的是,您应用名为 Hindley–Milner类型推断的流程。

一般原则包括三个步骤:

  1. 首先,我们将未确定类型(已编写'Z'Y'X等)分配给变量和表达式。

    • 如果表达式使用了我们已经为其分配了类型的变量,那么我们就使用它。例如,如果three已与int类型绑定,则在val nine = three * three中,我们知道three的类型为int
    • 如果表达式使用的变量我们已经分配了一个非常重要的 scheme (即,它是多态的),那么我们使用该方案,但用不确定的类型替换每个类型变量。例如,如果first已与'a * 'b -> 'a类型绑定,则在val firstOfFirst = fn quartet => first (first quartet)中,我们为first分配一个'Z * 'Y -> 'Z,另一个类型为'X * 'W -> 'W fn
    • 如果表达式使用新绑定的变量(由recfn f => (f 1, f "one")确定范围),那么该变量的所有出现都必须具有完全相同的类型 - 此时不允许多态。例如,在f(最终给出类型错误)中,我们最初将所有出现的'Z分配为单一类型int -> 'Y。 (因为我们以后需要对string -> 'Yval one = fn f => (fn x => f x)进行优化,所以会出现类型错误。这是因为标准ML不支持 first-class polymorphism 。)

    在您的示例f中,我们可以指定'Z类型x'Y类型f

  2. 接下来,我们执行类型统一,其中我们识别必须匹配的不同子表达式的不同部分。例如,如果我们知道'Z -> real的类型为iint的类型为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

  3. 最后,我们执行类型泛化。一旦我们执行了所有可以执行的统一 - 一旦我们推断出关于类型的所有内容 - 我们就可以安全地用绑定类型变量替换未确定的类型。在您的情况下,这允许我们为{{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

...现在你的所有功能都应该有相同的类型。