函数式编程的数学基础(七)简单类型论

1,436 阅读4分钟

Kleene-Rosser悖论

构造出Y组合子几乎证明了lambda系统具有图灵完备性,尽管在当年这样的理论发展还不完善。

但是任何具有图灵完备性的系统必定会受到图灵停机问题的困扰,这个问题的lambda版本就是 Kleene-Rosser 悖论。

为了便于理解,这里我们不采用原版的构造方法,我们会引入一些简化的概念来构造Kleene-Rosser悖论。

我们以Y组合子构造一个函数f:

f=λx.(NOT (f x))f = \lambda x.(\text{NOT}\ (f\ x))

可以发现,我们使得一个递归函数返回自身的否定,那么将会得到一个自相矛盾的函数。

这就是 Kleene-Rosser 悖论,它的存在导致 lambda 系统自身的不一致性。也导致包括邱奇本人、Rosser、Haskell等诸多优秀的数学和逻辑学家投入大量精力致力于修复这个问题。

但是在学术研究领域,有问题不一定是坏事,解决问题的过程中,诞生的新方法、新理论,可能价值反而超过解决问题本身。

Kleene-Rosser 悖论就是一个典型的案例。

简单类型论

为了解决Kleene-Rosser悖论,邱奇提出了为lambda函数引入类型。我们简要描述如下:

  • 基本类型:如 Integer、Boolean 这样的基本类型。其值可以自行定义,通常来自一些数学概念。
  • 函数类型:如 A -> B 这样的函数类型,部分常量函数可以自行定义,通常来自一些数学概念,亦可能由已有的类型通过lambda运算构成。
  • 类型运算:
    • 乘法类型 A×B,可理解为元组
    • 加法类型 A+B,可以理解为类型的或关系

在这样的定义下,任何lambda表达式都有确定的类型。

通过上述类型定义,一方面,lambda演算可以作为工具,给其它数学分支使用,另一方面因为无法构造不动点,不能产生递归类型,这也就解决了Kleene-Rosser悖论。但正因为无法构造不动点,带有简单类型的lambda演算不再是图灵完备的。后世又有诸多数学家、计算机学家尝试在简单类型论的基础上恢复图灵完备性,那是后话,暂且不表。

即使失去了图灵完备性,简单类型lambda演算也拥有巨大的潜力。由 Haskell Curry 提出,并由 Howard 发展的 Curry-Howard 同构,揭示了简单类型论与命题逻辑之间的深刻联系。

Curry-Howard 同构

为了理解 Curry-Howard 同构,我们首先来简单复习一下命题逻辑。

命题逻辑是一组基本的逻辑工具。它定义了“命题”、“与”、“或”、“蕴含”等概念。

例如,如果我们有三个命题A、B、C,现有两个证明

p1AB(若ABp2BC(若BCp1:A \rightarrow B(若A则B) \\ p2:B \rightarrow C(若B则C)

则可以构造出证明

p3AC(若ACp3:A \rightarrow C(若A则C)

而我们考虑在带类型的lambda中,如果有两个函数:

f1=λa.b:ABf2=λb.c:BCf_1 = \lambda a.b: A \rightarrow B \\ f_2 = \lambda b.c: B \rightarrow C \\

通过此二函数的复合运算可以构造出函数f

f=f2f1=λa.(f2 (f1 a)):ACf = f_2 \circ f_1 = \lambda a.(f_2\ (f_1\ a)): A \rightarrow C

Haskell Curry 发现命题逻辑和类型构造之间具有一种奇妙的联系:

在一个简单类型系统中,我们有一组对应于公理的简单类型AACA+B......(A、A \rightarrow C、A+B ......),如果我们能构造出构造出一些新类型BCAB......(B、C、A \rightarrow B......)的表达式,那么我们也能在命题逻辑中证明这些新类型对应的命题。

基本的命题逻辑符号和类型论符号的对应关系如下:

文字描述数学符号类型符号编程语言(JavaScript)
命题TTTT
A且B (析取)A  BA\ ∧\ BA+BA+BA | B
A或B (合取)A  BA\ ∨\ BA×BA \times B[A, B]
若A则B(蕴含)A  BA\ \rightarrow\ BABA \rightarrow BA => B

lambda演算的β归约对应于逻辑系统的分离规则

分离规则(MP) :如果有 AA 和 ABA \rightarrow B  ,那么有 BB 。

简单类型论中的基本类型,对应于在逻辑系统中的公理。

简单类型论中构造一个新的类型,对应于在逻辑系统中推导出一个新的定理。

编写一个lambda演算的表达式,对应于在逻辑系统中进行推导。

Curry-Howard 同构揭示了命题逻辑与带类型lambda演算之间的深刻联系,它也为型论脱离lambda体系发展打下了基础。

带类型的组合子逻辑

我们前文已经证明了组合子逻辑与lambda演算的等价性,那么简单类型论应用于组合子逻辑,Curry-Howard 同构会展现出什么样的特性呢?

我们考虑SKI系统的类型:

I=λx.x:XXK=λx.λy.x:X(YX)S=λx.λy.λz.(x z (y z)):(X(YZ))((XY)(XZ))\begin{array}{ll} I = \lambda x.x & : X \rightarrow X \\ K = \lambda x.\lambda y.x & : X \rightarrow (Y \rightarrow X) \\ S = \lambda x.\lambda y.\lambda z.(x\ z\ (y\ z)) & : (X \rightarrow (Y \rightarrow Z))\rightarrow((X \rightarrow Y)\rightarrow (X \rightarrow Z)) \\ \end{array}

若将SK的类型对应到命题逻辑,我们可以发现,它刚好是希尔伯特公理体系的两个公理。

我们前文已经讲到,I组合子可以由KS构造,这对应了希尔伯特公理体系中,命题等价于自身,可以由其它公理推导出来。

以此逻辑构造的逻辑公理体系被称作正蕴含逻辑(positive implicational logic),也译作蕴含命题演算

我们亦可知,BCKW和X组合子逻辑,也可以对应到命题逻辑的一组公理,有兴趣可以参考前篇自行推导。

扩展的 Curry-Howard 同构

在数学领域,命题逻辑是多数逻辑系统的基础,若我们为类型系统添加真值和否定逻辑,则可以得到布尔运算逻辑

文字描述数学符号类型符号编程语言(TypeScript)
false\bot (读作bottom)void
true\top (读作top)any
非T¬T\neg TTT \rightarrow \botT => void

为了支持否定逻辑,必须要添加一条公理:

(¬X¬Y)(YX)(\neg X \rightarrow \neg Y) \rightarrow (Y \rightarrow X)

其对应的基本类型是

(X)(Y)(YX)(X \rightarrow \bot) \rightarrow (Y \rightarrow \bot) \rightarrow (Y \rightarrow X)

此公理即所谓的逆否命题公理。我们在初等数学中使用的反证法,就依赖此条公理。在希尔伯特公理体系中,也有此公理。

我们还可以继续根据命题逻辑的上位系统扩展简单类型系统,比如添加谓词和量词逻辑扩展到一阶逻辑。其对应了\sum\prod 类型。但是这样就超出了简单类型论的范围了,包含这两种类型的类型论被称为”依赖类型论“,我们留待后文讨论。

结语

基于Curry-Howard同构建立的类型论,与lambda演算逐渐走上了不同的道路,在类型论中,我们更关心lambda表达式本身的结构,而不是对它应用归约求得结果。

从这个时间点开始,类型论的发展与lambda已经实际形成了竞争关系。

对于一个实际问题,我们既可以从类型论的角度把它抽象为“构造lambda表达式成为特定类型”,也可以从lambda演算的角度抽象为“计算特定lambda表达式的结果”。

接下来的系列中我们仍然回到lambda演算的发展,更多关于类型论的发展留待其它系列讨论。