推断类型似乎检测到无限循环,但实际发生了什么?

时间:2009-12-02 00:49:25

标签: haskell type-inference type-systems ml hindley-milner

在Andrew Koenig的An anecdote about ML type inference中,作者使用merge sort的实现作为ML的学习练习,很高兴找到“不正确”的类型推断。

  

令我惊讶的是,编译器报告了一种

'a list -> int list
     

换句话说,这个sort函数接受任何类型的列表并返回一个整数列表。

     

这是不可能的。输出必须是输入的排列;它怎么可能有不同的类型?读者肯定会发现我的第一个冲动:我想知道我是否在编译器中发现了一个错误!

     

在考虑了一些之后,我意识到还有另一种方法可以忽略它的论点:也许它根本没有返回。确实,当我尝试它时,这正是发生的事情:sort(nil)确实返回nil,但排序任何非空列表将进入无限递归循环。

翻译成Haskell时

split [] = ([], [])
split [x] = ([x], [])
split (x:y:xs) = (x:s1, y:s2)
  where (s1,s2) = split xs

merge xs [] = xs
merge [] ys = ys
merge xx@(x:xs) yy@(y:ys)
  | x < y     = x : merge xs yy
  | otherwise = y : merge xx ys

mergesort [] = []
mergesort xs = merge (mergesort p) (mergesort q)
  where (p,q) = split xs

GHC推断出类似的类型:

*Main> :t mergesort
mergesort :: (Ord a) => [t] -> [a]

Damas–Hindley–Milner algorithm如何推断这种类型?

2 个答案:

答案 0 :(得分:30)

这确实是一个了不起的例子;基本上,在编译时检测到一个无限循环!在这个例子中,Hindley-Milner推论没有什么特别之处;它只是照常进行。

请注意,ghc正确获取splitmerge的类型:

*Main> :t split
split :: [a] -> ([a], [a])
*Main> :t merge
merge :: (Ord t) => [t] -> [t] -> [t]

现在谈到mergesort时,对于某些类型t 1 <,它通常是一个函数t 1 →t 2 / sub>和t 2 。然后它看到第一行:

mergesort [] = []

并且意识到t 1 和t 2 必须是列表类型,比如t 1 = [t 3 ]和t 2 = [t 4 ]。因此mergesort必须是函数[t 3 ]→[t 4 ]。下一行

mergesort xs = merge (mergesort p) (mergesort q) 
  where (p,q) = split xs

告诉它:

  • xs必须是要分割的输入,即某些a的类型[a](对于a = t 3 ,它已经是a的类型)。
  • 所以pq的类型也是[t 3 ],因为split是[a]→([a],[a] )
  • 因此,
  • mergesort p(回想一下,mergesort被认为类型为[t 3 ]→[t 4 ])类型为[t] <子> 4 ]。
  • mergesort q的类型为[t 4 ],原因完全相同。
  • 由于merge的类型为(Ord t) => [t] -> [t] -> [t],而表达式merge (mergesort p) (mergesort q)中的输入均为[t 4 ]类型,因此类型为t 4 必须在Ord
  • 最后,merge (mergesort p) (mergesort q)的类型与其输入相同,即[t 4 ]。这符合mergesort以前已知的类型[t 3 ]→[t 4 ],因此不再需要进行推论和“统一” “Hindley-Milner算法的一部分已经完成。 mergesort的类型为[t 3 ]→[t 4 ],Ord中的t 4

这就是你得到的原因:

*Main> :t mergesort 
mergesort :: (Ord a) => [t] -> [a]

(以上关于逻辑推理的描述与算法的描述相同,但算法遵循的具体步骤顺序就是维基百科页面上给出的步骤。)

答案 1 :(得分:2)

可以推断出该类型,因为它看到您将mergesort的结果传递给merge,后者又将列表的头部与<进行比较,后者是{{1}}的一部分。 Ord类型类。因此类型推断可以推断它必须返回Ord实例的列表。当然,因为它实际上是无限地递归,所以我们无法推断出它实际上没有返回的类型。