GHC/GHCi 意外接受了代码
Code unexpectedly accepted by GHC/GHCi
我不明白为什么这段代码应该通过类型检查:
foo :: (Maybe a, Maybe b)
foo = let x = Nothing in (x,x)
因为每个组件都绑定到同一个变量 x
,我希望这个表达式的最通用类型是 (Maybe a, Maybe a)
。如果我使用 where
而不是 let
,我会得到相同的结果。我错过了什么吗?
简而言之,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)
相同的类型。
相反,lambda 不 具有相同的泛化步骤。相比之下,(\x -> (x, x)) Nothing
"only" 的类型为 forall t. (Maybe t, Maybe t)
,其中两个组件都被强制为同一类型。这里 x
再次被分配类型 Maybe t
,t
新鲜,但它不是一般化的。然后 (x, x)
被分配类型 (Maybe t, Maybe t)
。仅在顶层我们概括添加 forall t
,但此时获得异构对为时已晚。
我不明白为什么这段代码应该通过类型检查:
foo :: (Maybe a, Maybe b)
foo = let x = Nothing in (x,x)
因为每个组件都绑定到同一个变量 x
,我希望这个表达式的最通用类型是 (Maybe a, Maybe a)
。如果我使用 where
而不是 let
,我会得到相同的结果。我错过了什么吗?
简而言之,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)
相同的类型。
相反,lambda 不 具有相同的泛化步骤。相比之下,(\x -> (x, x)) Nothing
"only" 的类型为 forall t. (Maybe t, Maybe t)
,其中两个组件都被强制为同一类型。这里 x
再次被分配类型 Maybe t
,t
新鲜,但它不是一般化的。然后 (x, x)
被分配类型 (Maybe t, Maybe t)
。仅在顶层我们概括添加 forall t
,但此时获得异构对为时已晚。