haskell - 存在类型的理论基础是什么?

标签 haskell types type-systems existential-type quantifiers

Haskell Wiki很好地解释了如何使用存在类型,但我不太理解它们背后的理论。

考虑这个存在类型的例子:

data S = forall a. Show a => S a      -- (1)

为我们可以转换为String的事物定义类型包装器。 wiki 提到我们真正想要定义的是这样的类型

data S = S (exists a. Show a => a)    -- (2)

即真正的“存在”类型 - 松散地我认为这意味着“数据构造函数 S 接受 Show 实例存在的任何类型,并且包裹它”。事实上,您可能可以按如下方式编写 GADT:

data S where                          -- (3)
    S :: Show a => a -> S

我还没有尝试编译它,但看起来它应该可以工作。对我来说,GADT 显然等同于我们要编写的代码 (2)。

但是,我完全不明白为什么 (1) 等于 (2)。为什么将数据构造函数移到外部会将 forall 变成 exists

我能想到的最接近的是De Morgan's Laws在逻辑中,交换否定和量词的顺序会将存在量词变成全称量词,反之亦然:

¬(∀x. px) ⇔ ∃x. ¬(px)

但是数据构造函数似乎与否定运算符完全不同。

使用 forall 而不是不存在的 exists 定义存在类型的能力背后的理论是什么?

最佳答案

首先,看一下“库里·霍华德对应表”,其中指出计算机程序中的类型对应于直觉逻辑中的公式。直觉逻辑就像你在学校学到的“常规”逻辑一样,但没有排除中间或双重否定消除法则:

  • 不是公理:P ⇔ ØØP(但 P ⇒ ØØP 也可以)

  • 不是公理:P ∨ ØP

逻辑定律

您对德摩根定律的理解是正确的,但首先我们将使用它们来推导出一些新的定律。德摩根定律的相关版本是:

  • ∀x。 P(x) = ∃x。 ØP(x)
  • ∃x。 P(x) = ∀x。 ØP(x)

我们可以推导出 (∀x.P ⇒ Q(x)) = P ⇒ (∀x.Q(x)):

  1. (∀x.P⇒Q(x))
  2. (∀x. ØP ∨ Q(x))
  3. ØP ∨ (∀x.Q(x))
  4. P ⇒ (∀x.Q(x))

并且 (∀x.Q(x) ⇒ P)  = (∃x.Q(x)) ⇒ P (下面使用这个):

  1. (∀x.Q(x)⇒P)
  2. (∀x. ØQ(x) ∨ P)
  3. (ØØ∀x.ØQ(x))∨P
  4. (∃x.Q(x)) ∨ P
  5. (∃x.Q(x))⇒P

请注意,这些定律在直觉逻辑中也成立。下面的论文引用了我们推导出的两条定律。

简单类型

最简单的类型很容易使用。例如:

data T = Con Int | Nil

构造函数和访问器具有以下类型签名:

Con :: Int -> T
Nil :: T

unCon :: T -> Int
unCon (Con x) = x

类型构造函数

现在让我们来处理类型构造函数。采用以下数据定义:

data T a = Con a | Nil

这会创建两个构造函数,

Con :: a -> T a
Nil :: T a

当然,在 Haskell 中,类型变量是隐式通用量化的,所以这些实际上是:

Con :: ∀a. a -> T a
Nil :: ∀a. T a

访问器也同样简单:

unCon :: ∀a. T a -> a
unCon (Con x) = x

量化类型

让我们将存在量词 ∃ 添加到我们的原始类型(第一个类型,没有类型构造函数)。与其将其引入到类型定义中(这看起来不像逻辑),不如将其引入到构造函数/访问器定义中(这看起来确实像逻辑)。我们稍后将修复数据定义以匹配。

而不是 Int ,我们现在将使用 ∃x. t 。在这里,t是某种类型表达式。

Con :: (∃x. t) -> T
unCon :: T -> (∃x. t)

根据逻辑规则(上面第二条规则),我们可以重写 Con 的类型至:

Con :: ∀x. t -> T

当我们将存在量词移到外部(prenex形式)时,它就变成了全称量词。

因此,以下内容在理论上是等效的:

data T = Con (exists x. t) | Nil
data T = forall x. Con t | Nil

除了 exists 没有语法在 haskell 。

在非直觉逻辑中,允许从 unCon 的类型导出以下内容: :

unCon :: ∃ T -> t -- invalid!

这是无效的,因为这种转换在直觉逻辑中是不允许的。所以不可能写unCon的类型没有exists关键字,并且不可能将类型签名放在 prenex 形式中。很难让类型检查器保证在这种情况下终止,这就是 Haskell 不支持任意存在量词的原因。

来源

“具有类型推断的一流多态性”,Mark P. Jones,第 24 届 ACM SIGPLAN-SIGACT 编程语言原理研讨会论文集 ( web )

关于haskell - 存在类型的理论基础是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10753073/

相关文章:

scala - 方差注释,通过Scala编译器跟踪 "positive"和 "negative"位置

haskell - 在签名中约束构造函数

java - 浮点 setter 设置任意分数

haskell - 在 Haskell 中提升类实例

scala - 学习scala的小而好的scala项目——尤其是函数式编程和类型系统

c++ - 为什么 C++ 的 void 类型只是三心二意的单元类型?

bash - 是否可以直接从 Bash 调用 Haskell 代码并输出到标准输出?

haskell - 哪个映射必须包含所有可能的键?

database - 获取记录字段的文本表示?

json - 将 JSON 大整数编码为 float 的幂