20

我对类型的存在量化是什么以及可以在哪里使用有一个大致的了解。然而,根据我迄今为止的经验,为了有效地使用这个概念,需要理解很多警告。

问题:是否有任何好的资源来解释 GHC 中如何实施存在量化?IE

  • 存在类型的统一是如何工作的——什么是统一的,什么不是?
  • 对类型的后续操作以什么顺序执行?

我的目标是更好地理解 GHC 向我抛出的错误信息。消息通常会说一些类似的东西"this type using forall and this other type don't match",但是他们没有解释为什么会这样。

4

1 回答 1

20

Simon Peyton-Jones 的论文中涵盖了细节,尽管需要大量的技术专业知识才能理解它们。如果你想阅读一篇关于 Haskell 类型推断如何工作的论文,你应该阅读广义代数数据类型 (GADT),它结合了存在类型和其他几个特性。我建议在http://research.microsoft.com/en-us/people/simonpj/的论文列表中的“完整和可判定的 GADT 类型推断” 。

存在量化实际上很像全称量化。这是一个突出两者之间相似之处的示例。该函数useExis没用,但它仍然是有效的代码。

data Univ a = Univ a
data Exis = forall a. Exis a

toUniv :: a -> Univ a
toUniv = Univ

toExis :: a -> Exis
toExis = Exis

useUniv :: (a -> b) -> Univ a -> b
useUniv f (Univ x) = f x

useExis :: (forall a. a -> b) -> Exis -> b
useExis f (Exis x) = f x

首先,请注意toUnivtoExis几乎相同。它们都有一个自由类型参数a,因为两个数据构造函数都是多态的。但是虽然a出现在 的返回类型中toUniv,却没有出现在 的返回类型中toExis。当谈到使用数据构造函数可能会遇到的类型错误时,存在类型和通用类型之间没有太大区别。

其次,注意 rank-2forall a. a -> b中的类型useExis。这是类型推断的最大区别。从模式匹配中获取的存在类型(Exis x)就像一个额外的隐藏类型变量传递给函数体,它不能与其他类型统一。为了使这一点更清楚,这里是最后两个函数的一些错误声明,我们试图统一不应该统一的类型。在这两种情况下,我们都强制 的类型x与不相关的类型变量统一。在useUniv中,类型变量是函数类型的一部分。在useExis中,它是数据结构中的存在类型。

useUniv' :: forall a b c. (c -> b) -> Univ a -> b
useUniv' f (Univ x) = f x -- Error, can't unify 'a' with 'c'
                          -- Variable 'a' is there in the function type

useExis' :: forall b c. (c -> b) -> Exis -> b
useExis' f (Exis x) = f x -- Error, can't unify 'a' with 'c'.
                          -- Variable 'a' comes from the pattern "Exis x",
                          -- via the existential in "data Exis = forall a. Exis a".
于 2012-02-13T16:39:49.357 回答