GHC / GHCi意外接受的代码

时间:2018-07-27 20:44:33

标签: haskell ghc type-inference typechecking

我不明白为什么这段代码应该通过类型检查:

foo :: (Maybe a, Maybe b)
foo = let x = Nothing in (x,x)

由于每个组件都绑定到相同的变量x,所以我希望此表达式的最通用类型是(Maybe a, Maybe a)。如果我使用where而不是let,则会得到相同的结果。我想念什么吗?

1 个答案:

答案 0 :(得分:21)

简而言之,x的类型由let概括。这是Hindley-Milner类型推断算法中的关键步骤。

具体来说,let x = Nothing首先为x分配了类型Maybe t,其中t是新鲜的类型变量。然后,对类型进行泛化,通用地量化其所有类型变量(从技术上讲:除了在其他地方使用的变量,但这里我们只有t)。这导致x :: forall t. Maybe t。请注意,此类型与Nothing :: forall t. Maybe t完全相同。

因此,每次我们在代码中使用x时,它都指的是潜在不同的类型Maybe t,就像Nothing一样。因此,使用(x, x)(Nothing, Nothing)的类型相同。

相反,lambdas不具有相同的泛化步骤。相比之下,(\x -> (x, x)) Nothing“仅”具有类型forall t. (Maybe t, Maybe t),其中两个组件都必须具有相同的类型。在这里,x再次被分配了类型Maybe t,其中t是新鲜的,但是没有被概括。然后为(x, x)分配了类型(Maybe t, Maybe t)。仅在最高级别上,我们才一般性地添加forall t,但是到那时为时已晚,无法获得异构对。