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

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演算的发展,更多关于类型论的发展留待其它系列讨论。

相关推荐
小妖66622 分钟前
el-breadcrumb 面包屑第一项后面怎么写没有分隔符
javascript·vue.js·elementui
2401_8960081929 分钟前
GCC 使用说明
前端·javascript·算法
ᖰ・◡・ᖳ7 小时前
JavaScript:PC端特效--缓动动画
开发语言·前端·javascript·css·学习·html5
布Coder9 小时前
前端 vue + element-ui 框架从 0 - 1 搭建
前端·javascript·vue.js
小刘不知道叫啥11 小时前
简单说一下 Webpack分包
前端·javascript·webpack·node.js
MessiGo11 小时前
Javascript 编程基础(2)基础知识 | 2.1、javascript与Node.js
开发语言·javascript·node.js
前端达人13 小时前
React 播客专栏 Vol.13|样式不难搞,Tailwind CSS 与 SVG 实战入门
前端·javascript·css·react.js·前端框架
W.Y.B.G13 小时前
vue3 vite 项目中自动导入图片
前端·javascript·vue.js
阳光开朗大男孩 = ̄ω ̄=13 小时前
【Vue篇】潮汐中的生命周期观测站
前端·javascript·vue.js
邝邝邝邝丹14 小时前
React学习———Immer 和 use-immer
javascript·学习·react.js