最早,我是在中学时读过的一本《可计算性与计算复杂性》的书中,粗略地了解了 lambda 演算。但那时我对它不甚了了,只觉得书中很多以它为基础的理论的推导过程,十分神奇。

后来,我在接触 emacs 编辑器和 Lisp 语言后,受社群风气影响,对 Haskell 进行了一番涉猎,并重新了解了作为 Haskell 语言理论基础的 lambda 演算。然而那时,我已经对 lambda 演算有了不同的看法,故而在此组织了这篇文章。

lambda 演算,作为可计算数理论的基础之一,其神奇之处,我个人认为主要有两点:

  1. lambda 演算系统中的所有数据,都是函数。(是的,包括表示真与假的布尔逻辑值 True 与 False,它们是函数)
  2. 任何 lambda 演算系统中的函数,都有不动点。(也就是对于任何函数 A,都存在 x 使得 A(x)=x,我们通常可以利用 Y 组合子,从 A 构造出 x 的形式)

如果是不懂 lambda 演算,且想了解它那「优美理论」是如何做到这两点的读者,可以用各种途径去收集关于它的资料,毕竟它已经是计算机科学中的基础理论之一。

但在此,如标题所言,lambda 演算的「优美理论」,相比我在 理论上最好的编程语言: 哲学基础篇 里做出的 DS -> RDS = 函数 + 状态机 简单分类法,更复杂,也偏离了编程的本质,是一套形式化却缺乏实际价值的理论。

我给它的判断是「知识系统的熵增」,在此,我先针对它那神奇的两点进行说明:

1. lambda 演算系统中的所有数据,都是函数。(是的,包括表示真与假的布尔逻辑值 True 与 False,它们是函数)

这其实没有什么好奇怪的,我们可以定义一个输入只有一个布尔变数的函数集合,然后再把所有输入为 True 的函数调用划分出来

(函数一 True)
(函数二 True)
……

我们实际上可以得到一个形式为 (__ True) 的「函数」,这个函数还是个高阶函数,你必须在下划线处输入一个函数才能得到结果。通过这种方式,的确可以在概念上将任何数据当成函数,但在编程实际中,有必要给一个普通的数据结构设定一个可以输入函数,并根据函数计算值的介面吗?

2. 任何 lambda 演算系统中的函数,都有不动点。(也就是对于任何函数 A,都存在 x 使得 A(x)=x,我们通常可以利用 Y 组合子,从 A 构造出 x 的形式)

这其实意味著,lambda 演算系统里的任何方程 G(x) = 0 都有一个解的计算形式——然而,为什么哥德巴赫猜想至今悬而未决呢?代数里,为何还有那么多无解的方程呢?认真分析 lambda 演算里 Y 组合子构造出的那些「解」,你就会发现这些「解」要么是一些「函数回文」,要么就超出了使方程成立的代数数域。

我一直以为「可计算数」的意思是指哪些数是可计算出来的(或逼近),令我们进而了解哪些数是不可计算出来的(或逼近)。但实际上,lambda 演算系统里的很多数往往徒有计算形式,而且实际上也不存在判断这些计算形式是否真的可行的通用办法(图灵停机问题)。

而 RDS = 函数 + 状态机 这样的分类法,实际上也是对 lambda 演算系统进行了质问:「状态机可以看作是函数吗?」

当然,对于 lambda 演算这样的理论,实际上是可以将状态机当作是函数的,毕竟状态机每一步的执行其实也是一个函数 (Y, Q)=M(X, Q),X 代表输入,Y 代表输出,Q 代表状态。

但是,如果你将状态机放入 lambda 演算理论里拆解,实际上是将一个简单概念复杂化,将编程的形式变成一种智力游戏,让我们偏离我们要用编程来解决的,问题的本质——

好的形式正确表达内容,而坏的形式喧宾夺主。

而且, lambda 演算解释不了,是什么,驱动状态机不断去执行下一步函数。这实际上在 lambda 演算系统之外——那便是在用 lambda 演算系统做推算的人本身。宇宙催生了人类,而人类又缔造了宇宙,这两个状态机的交互,正是一个不断延伸的矛盾螺旋。

所以,状态机是如此重要,不应被归入函数里。

因此,我选择 DS -> RDS = 函数 + 状态机 的分类法来探讨 OOWA 的设计,扬弃了 lambda 演算的那套理论。

推荐阅读:

相关文章