Haskell类型检查和确定性

时间:2018-08-10 11:15:42

标签: haskell hindley-milner

根据Haskell 2010 language report,其类型检查器基于Hindley-Milner。因此,请考虑这种类型的函数f

f :: forall a. [a] -> Int

例如,它可以是长度函数。根据Hindley-Milner的说法,f []的类型检查为Int。我们可以通过实例化f[Int] -> Int的类型以及[][Int]的类型来证明这一点,然后得出应用程序([Int] -> Int) [Int]的类型Int

在这个证明中,我选择通过将forall a. [a] -> Int替换为forall a. [a]来实例化类型Inta。我可以代替Bool,证明也可以。在Hindley-Milner中,我们可以将多态类型应用于另一个,而不指定我们使用哪些实例,这并不奇怪吗?

更具体地说,Haskell中的哪些内容阻止了我在a的实现中使用类型f?我可以想象f是一个在任何18上都等于[Bool]的函数,并且在所有其他类型的列表上都等于通常的长度函数。在这种情况下,f []18还是0? Haskell报告指出“未正式指定内核”,因此很难说出来。

2 个答案:

答案 0 :(得分:3)

在类型推断期间,此类类型变量确实可以实例化为任何类型。这可能被认为是高度不确定的步骤。

GHC在这种情况下使用内部的Any类型是有价值的。例如,编译

{-# NOINLINE myLength #-}
myLength :: [a] -> Int
myLength = length

test :: Int
test = myLength []

产生以下核心:

-- RHS size: {terms: 3, types: 4, coercions: 0}
myLength [InlPrag=NOINLINE] :: forall a_aw2. [a_aw2] -> Int
[GblId, Str=DmdType]
myLength =
  \ (@ a_aP5) -> length @ [] Data.Foldable.$fFoldable[] @ a_aP5

-- RHS size: {terms: 2, types: 6, coercions: 0}
test :: Int
[GblId, Str=DmdType]
test = myLength @ GHC.Prim.Any (GHC.Types.[] @ GHC.Prim.Any)

GHC.Prim.Any出现在最后一行。

现在,这真的不是确定性的吗?好吧,它确实在算法的“中间”涉及一种不确定的步骤,但是最终的结果(最通用)类型是Int,并且在确定性上如此。为a选择哪种类型都没有关系,我们总是在最后得到类型Int

当然,获得相同的类型是不够的:我们还希望获得相同的Int值。我猜想可以证明,给定

f :: forall a. T a
g :: forall a. T a -> U

然后

g @ V (f @ V) :: U
不管类型V

都是相同的值。这应该是parametricity应用于那些多态类型的结果。

答案 1 :(得分:3)

要进一步了解Chi的答案,以下是f []不能依赖于f[]的类型实例的证明。根据免费的定理(上一篇文章here), 对于任何类型的a,a'和任何函数g :: a -> a',然后

f_a = f_a' . map g

其中f_a是类型fa的实例,例如在Haskell中

f_Bool :: [Bool] -> Int
f_Bool = f

然后,如果您评估[]_a上的先前相等,则得出f_a []_a = f_a' []_a'。对于原始问题,f_Int []_Int = f_Bool []_Bool

Haskell中的一些参量参考也很有用,因为Haskell看起来比Walder论文中描述的多态Lambda演算更强大。特别是,wiki page说,可以通过使用seq函数在Haskell中打破参数化。

Wiki页面还说我存在类型依赖的函数(用Haskell以外的其他语言),它称为ad-hoc polymorphism