欣德利 - 米尔纳的概括变坏了?

sin*_*law 2 haskell type-inference hindley-milner

考虑以下程序(在Haskell中,但可以是任何HM推断语言):

x = []
y = x!!0
Run Code Online (Sandbox Code Playgroud)

使用HM(或运行编译器),我们推断:

x :: forall t. [t]
y :: forall a. a
Run Code Online (Sandbox Code Playgroud)

我理解这是如何发生的,通过通常的泛化/实例化规则,但我不确定是否有可能有这样的东西forall a. a.

一个问题是:由于我们在这里有一个越界访问,人们可以排除该程序作为一个有效的例子.相反,我们可以说我们推断的通用类型是程序中出错的标志吗?如果是的话,我们是否可以使用这个"事实"故意在其他情况下无效检查无效程序?

下一个程序甚至可以获得更奇怪的类型:

c = []
d = (c!!0) + (1 :: Int)
Run Code Online (Sandbox Code Playgroud)

推断类型:

c :: forall t. [t]
d :: Int
Run Code Online (Sandbox Code Playgroud)

......虽然d是从中抽出来的c!

我们可以在没有排除有效程序的情况下增强HM在这里做得更好吗?

编辑:我怀疑ed这是使用部分函数的工件(!!0在这种情况下).但请看:

c = []
d = case c of [] -> 0; (x:_) -> x + (1 :: Int)
Run Code Online (Sandbox Code Playgroud)

现在没有使用部分功能.然而,c :: forall t. [t]d :: Int.

Han*_*Lub 5

Hindley-Milner类型的术语不依赖于其子项的,仅取决于它们的类型.HM类型检查器永远不会评估表达式,只会对它们进行类型检查,因此它将您x视为"列表a",而不是"空列表a",就像人们非正式地对您的程序进行类型检查一样.

有一些类型系统可以将你的程序标记为类型不正确,例如依赖类型,但那些没有显式类型声明的类型推断,这是Haskell/ML程序员喜欢的奢侈品之一,这要归功于HM.

使用HM(GADT)的扩展Haskell可以为"安全列表"定义类型

data Empty
data NonEmpty

data SafeList a b where
  Nil :: SafeList a Empty
  Cons:: a -> SafeList a b -> SafeList a NonEmpty

(!!) :: SafeList a NonEmpty -> Int -> a
-- etc
Run Code Online (Sandbox Code Playgroud)

这会产生Nil!!0类型错误.