Kleene-Rosser悖论
构造出Y组合子几乎证明了lambda系统具有图灵完备性,尽管在当年这样的理论发展还不完善。
但是任何具有图灵完备性的系统必定会受到图灵停机问题的困扰,这个问题的lambda版本就是 Kleene-Rosser 悖论。
为了便于理解,这里我们不采用原版的构造方法,我们会引入一些简化的概念来构造Kleene-Rosser悖论。
我们以Y组合子构造一个函数f:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> f = λ x . ( NOT ( f x ) ) f = \lambda x.(\text{NOT}\ (f\ x)) </math>f=λx.(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,现有两个证明
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> p 1 : A → B (若 A 则 B ) p 2 : B → C (若 B 则 C ) p1:A \rightarrow B(若A则B) \\ p2:B \rightarrow C(若B则C) </math>p1:A→B(若A则B)p2:B→C(若B则C)
则可以构造出证明
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> p 3 : A → C (若 A 则 C ) p3:A \rightarrow C(若A则C) </math>p3:A→C(若A则C)
而我们考虑在带类型的lambda中,如果有两个函数:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> f 1 = λ a . b : A → B f 2 = λ b . c : B → C f_1 = \lambda a.b: A \rightarrow B \\ f_2 = \lambda b.c: B \rightarrow C \\ </math>f1=λa.b:A→Bf2=λb.c:B→C
通过此二函数的复合运算可以构造出函数f
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> f = f 2 ∘ f 1 = λ a . ( f 2 ( f 1 a ) ) : A → C f = f_2 \circ f_1 = \lambda a.(f_2\ (f_1\ a)): A \rightarrow C </math>f=f2∘f1=λa.(f2 (f1 a)):A→C
Haskell Curry 发现命题逻辑和类型构造之间具有一种奇妙的联系:
在一个简单类型系统中,我们有一组对应于公理的简单类型 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( A 、 A → C 、 A + B . . . . . . ) (A、A \rightarrow C、A+B ......) </math>(A、A→C、A+B......),如果我们能构造出构造出一些新类型 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( B 、 C 、 A → B . . . . . . ) (B、C、A \rightarrow B......) </math>(B、C、A→B......)的表达式,那么我们也能在命题逻辑中证明这些新类型对应的命题。
基本的命题逻辑符号和类型论符号的对应关系如下:
文字描述 | 数学符号 | 类型符号 | 编程语言(JavaScript) |
---|---|---|---|
命题T | T | T | T |
A且B (析取) | <math xmlns="http://www.w3.org/1998/Math/MathML"> A ∧ B A\ ∧\ B </math>A ∧ B | <math xmlns="http://www.w3.org/1998/Math/MathML"> A + B A+B </math>A+B | A | B |
A或B (合取) | <math xmlns="http://www.w3.org/1998/Math/MathML"> A ∨ B A\ ∨\ B </math>A ∨ B | <math xmlns="http://www.w3.org/1998/Math/MathML"> A × B A \times B </math>A×B | [A, B] |
若A则B(蕴含) | <math xmlns="http://www.w3.org/1998/Math/MathML"> A → B A\ \rightarrow\ B </math>A → B | <math xmlns="http://www.w3.org/1998/Math/MathML"> A → B A \rightarrow B </math>A→B | A => B |
lambda演算的β归约 对应于逻辑系统的分离规则:
分离规则(MP) :如果有 <math xmlns="http://www.w3.org/1998/Math/MathML"> A A </math>A 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> A → B A \rightarrow B </math>A→B ,那么有 <math xmlns="http://www.w3.org/1998/Math/MathML"> B B </math>B 。
简单类型论中的基本类型,对应于在逻辑系统中的公理。
简单类型论中构造一个新的类型,对应于在逻辑系统中推导出一个新的定理。
编写一个lambda演算的表达式,对应于在逻辑系统中进行推导。
Curry-Howard 同构揭示了命题逻辑与带类型lambda演算之间的深刻联系,它也为型论脱离lambda体系发展打下了基础。
带类型的组合子逻辑
我们前文已经证明了组合子逻辑与lambda演算的等价性,那么简单类型论应用于组合子逻辑,Curry-Howard 同构会展现出什么样的特性呢?
我们考虑SKI系统的类型:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> I = λ x . x : X → X K = λ x . λ y . x : X → ( Y → X ) S = λ x . λ y . λ z . ( x z ( y z ) ) : ( X → ( Y → Z ) ) → ( ( X → Y ) → ( X → Z ) ) \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} </math>I=λx.xK=λx.λy.xS=λx.λy.λz.(x z (y z)):X→X:X→(Y→X):(X→(Y→Z))→((X→Y)→(X→Z))
若将SK的类型对应到命题逻辑,我们可以发现,它刚好是希尔伯特公理体系的两个公理。
我们前文已经讲到,I组合子可以由KS构造,这对应了希尔伯特公理体系中,命题等价于自身,可以由其它公理推导出来。
以此逻辑构造的逻辑公理体系被称作正蕴含逻辑(positive implicational logic) ,也译作蕴含命题演算。
我们亦可知,BCKW和X组合子逻辑,也可以对应到命题逻辑的一组公理,有兴趣可以参考前篇自行推导。
扩展的 Curry-Howard 同构
在数学领域,命题逻辑是多数逻辑系统的基础,若我们为类型系统添加真值和否定逻辑,则可以得到布尔运算逻辑
文字描述 | 数学符号 | 类型符号 | 编程语言(TypeScript) |
---|---|---|---|
假 | false | <math xmlns="http://www.w3.org/1998/Math/MathML"> ⊥ \bot </math>⊥ (读作bottom) | void |
真 | true | <math xmlns="http://www.w3.org/1998/Math/MathML"> ⊤ \top </math>⊤ (读作top) | any |
非T | <math xmlns="http://www.w3.org/1998/Math/MathML"> ¬ T \neg T </math>¬T | <math xmlns="http://www.w3.org/1998/Math/MathML"> T → ⊥ T \rightarrow \bot </math>T→⊥ | T => void |
为了支持否定逻辑,必须要添加一条公理:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> ( ¬ X → ¬ Y ) → ( Y → X ) (\neg X \rightarrow \neg Y) \rightarrow (Y \rightarrow X) </math>(¬X→¬Y)→(Y→X)
其对应的基本类型是
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> ( X → ⊥ ) → ( Y → ⊥ ) → ( Y → X ) (X \rightarrow \bot) \rightarrow (Y \rightarrow \bot) \rightarrow (Y \rightarrow X) </math>(X→⊥)→(Y→⊥)→(Y→X)
此公理即所谓的逆否命题公理。我们在初等数学中使用的反证法,就依赖此条公理。在希尔伯特公理体系中,也有此公理。
我们还可以继续根据命题逻辑的上位系统扩展简单类型系统,比如添加谓词和量词逻辑扩展到一阶逻辑 。其对应了 <math xmlns="http://www.w3.org/1998/Math/MathML"> ∑ \sum </math>∑ 和 <math xmlns="http://www.w3.org/1998/Math/MathML"> ∏ \prod </math>∏ 类型。但是这样就超出了简单类型论的范围了,包含这两种类型的类型论被称为"依赖类型论",我们留待后文讨论。
结语
基于Curry-Howard同构建立的类型论,与lambda演算逐渐走上了不同的道路,在类型论中,我们更关心lambda表达式本身的结构,而不是对它应用归约求得结果。
从这个时间点开始,类型论的发展与lambda已经实际形成了竞争关系。
对于一个实际问题,我们既可以从类型论的角度把它抽象为"构造lambda表达式成为特定类型",也可以从lambda演算的角度抽象为"计算特定lambda表达式的结果"。
接下来的系列中我们仍然回到lambda演算的发展,更多关于类型论的发展留待其它系列讨论。