我不明白为什么这段代码应该通过类型检查:
foo :: (Maybe a, Maybe b)
foo = let x = Nothing in (x,x)
由于每个组件都绑定到相同的变量x
,所以我希望此表达式的最通用类型是(Maybe a, Maybe a)
。如果我使用where
而不是let
,则会得到相同的结果。我想念什么吗?
答案 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
,但是到那时为时已晚,无法获得异构对。