什么是完全类型推断的语言?这种语言的局限性?

时间:2012-05-05 13:48:03

标签: types programming-languages functional-programming type-inference type-systems

据我所知,任何编程语言都不需要在编写函数或模块时在源代码中编写类型注释,如果该代码块是“类型正确的”,编译器将推断类型和编译代码。还有更多吗?

是否有这样的语言?如果是,它的类型系统有任何限制吗?

更新1: 只是为了真的清楚,我问的是静态类型,完全类型推断的编程语言而不是动态类型编程语言。

3 个答案:

答案 0 :(得分:14)

什么是类型推断?

历史上,类型推断(或类型重构)意味着可以派生程序中的所有类型,而基本上不需要任何显式类型注释。然而,近年来,在编程语言主流中已经成为时尚,甚至将最简单的自下而上类型推导形式标记为“类型推断”(例如,C ++ 11的新auto声明。 )。所以人们已经开始添加“完整”来引用“真实”的东西。

什么是完整的类型推断?

语言可以在多大程度上推断类型,并且在实践中,几乎没有语言支持最严格意义上的“完整”类型推断(核心ML是唯一的例子)。但主要的区别因素是,是否可以为没有附加“定义”的绑定派生类型 - 特别是函数的参数。如果你可以写,比方说,

f(x) = x + 1

并且类型系统计算出例如类型Int→Int,那么调用这种类型的推理是有意义的。此外,我们在讨论多态类型推断时,例如,

g(x) = x

自动分配通用类型∀(t)t→t。

类型推断是在简单类型的lambda演算的背景下发明的,并且多态类型推断(也就是20世纪70年代发明的Hindley / Milner类型推断)是声称ML系列语言(Standard ML, OCaml,可以说是Haskell)。

完整类型推断的限制是什么?

Core ML具有“完整”多态类型推断的奢侈品。但它取决于其类型系统中多态性的某些限制。特别是,只有定义可以是通用的,而不是函数参数。也就是说,

id(x) = x;
id(5);
id(True)

工作正常,因为id在定义已知时可以赋予多态类型。但

f(id) = (id(5); id(True))

不会在ML中键入check,因为id不能作为函数参数进行多态化。换句话说,类型系统确实允许多态类型,如∀(t)t→t,但不是所谓的高级多态类型,如(∀(t)t→t)→Bool,其中多态值用于一流的方式(其中很明显,甚至很少有明确的类型语言允许)。

显式类型化的多态性lambda演算(也称为“System F”)允许后者。但它是类型理论的标准结果,完整系统F的类型重建是不可判定的。欣德利/米尔纳击中了一个表现较差的类型系统的甜蜜点,类型重建仍然是可以判断的。

还有更多高级类型系统功能也使完整类型重建不可判定。并且还有其他一些可以保持其可判定但仍然使其不可行,例如ad-hoc重载或子类型的存在,因为这会导致组合爆炸。

答案 1 :(得分:11)

完整类型推断的局限性在于它不适用于许多高级类型系统功能。作为一个例子,考虑Haskell和OCaml。这两种语言都几乎完全类型推断,但有一些功能可能会干扰类型推断。


在Haskell中,它的类型类与多态返回类型相结合:

readAndPrint str = print (read "asd")

此处readRead a => String -> a类型的函数,这意味着“对于支持类型类a的任何类型Read,函数read可以采用一个String并返回一个a。所以如果f是一个接受int的方法,我可以写f (read "123")并将它“123”转换为Int 123和用它调用f。它知道它应该将字符串转换为Int,因为f接受一个I​​nt。如果f取一个int列表,它会尝试将字符串转换为Ints列表相反。没问题。

但对于上面的readAndPrint函数,该方法不起作用。这里的问题是print可以接受任何可以打印的类型的参数(即支持Show类型类的任何类型)。因此,编译器无法知道您是否要将字符串转换为int,或int列表或其他任何可以打印的内容。所以在这种情况下你需要添加类型注释。


在OCaml中,有问题的特性是类中的多态函数:如果定义一个以对象作为参数并在该对象上调用方法的函数,编译器将推断该方法的单态类型。例如:

let f obj = obj#meth 23 + obj#meth 42

这里编译器将推断obj必须是具有类型meth的名为int -> int的方法的类的实例,即采用Int并返回Int的方法。您现在可以定义一组具有此类方法的类,并将该类的实例作为f的参数传递。没问题。

如果使用类型为'a. 'a -> int的方法定义类,则会出现问题,即可以采用任何类型的参数并返回int的方法。您不能将该类的对象作为参数传递给f,因为它与推断类型不匹配。如果您希望f将此类对象作为其参数,则唯一的方法是将类型注释添加到f


所以这些是几乎完全类型推断的语言的例子,以及它们不是的情况。如果您从这些语言中删除有问题的功能,则可以完全按类型推断。

因此,没有这些高级特征的ML的基本方言是完全类型推断的。例如,我假设Caml Light是完全类型推断的,因为它基本上没有类的OCaml(但我实际上并不知道语言,所以这只是一个假设)。

答案 2 :(得分:1)

另一个限制是排名较高的类型。 例如,以下程序在ML样式类型推断的语言中 typecheck:

foo = let bar f = (f ['a', 'b', 'c'], f [1,2,3]) in bar reverse

类型检查器可以为f指定类型[Char] - > [Char]或[Int] - > [Int],但不是forall a。[a] - > [a]。 在ML,Ocaml和F#中没有办法解决这个问题,因为你甚至无法编写更高级别的类型。

但是Haskell(通过GHC扩展)和Frege支持更高级别的类型。但是因为只有更高级别的类型检查(而不是更高级别的类型推断)是可能的,所以程序员需要提供类型注释,例如:

foo = let bar (f :: forall a.[a]->[a]) = (f ['a', 'b', 'c'], f [1,2,3]) in bar reverse