第16章:线性逻辑与资源——每个假设只能用一次
经典逻辑默认资源无限。这是一个谎言,而且是一个代价高昂的谎言。
第15章留下了一个令人不安的结论:只要系统足够强,就一定存在它无法证明的真命题;系统本身也无法担保自己的一致性。这是哥德尔的礼物——或者说,他的诅咒。
但"足够强"是什么意思?强在哪里?
一个形式系统之所以强大,部分原因在于它的假设可以被任意重复使用。你知道
这条规则看起来是常识。但它是可以质疑的。
1987 年,让-伊夫·吉拉尔(Jean-Yves Girard)问了一个简单的问题:如果拿掉收缩规则,会发生什么?
发生的事情远比他最初预期的要多。他得到的不只是一个削弱版的经典逻辑,而是一个全新的逻辑——线性逻辑——它精确地描述了资源被消耗的世界。
16.1 收缩规则在干什么
回忆收缩规则的形式:
横线上:上下文里有两份
这条规则说的是:你知道
在经典逻辑里,这完全合理。知识不是物质,你知道"
但世界上不是所有"前提"都是知识。
考虑这个句子:"我有一张五元钱,可以买一杯咖啡。"
这是一个完全合法的命题推断:有五元钱
但如果你对这个命题应用收缩,就得到:有一张五元钱,可以买两杯咖啡。这显然错了。那张钱被用掉了。
经典逻辑对这种错误无能为力,因为它根本不区分"可以重复使用的知识"和"使用后消耗的资源"。线性逻辑的出发点正是:这两种东西根本不一样,需要不同的符号和不同的规则。
兔狲教授评
经典逻辑处理知识,线性逻辑处理资源——这个区分是真实的,不是花哨的哲学。你写程序的时候内存会耗尽,量子态不能克隆,协议里的消息发出去就没了。经典逻辑对这些场景的建模从一开始就是错的。不是近似错误,是根本错误。
逻辑是在描述世界,还是在规范推断?
这里有一个哲学分歧值得停下来想一想。有人会说:逻辑不是在描述资源消耗,它是在谈论真值,而真值不会被"使用"耗尽。这个观点没有错——经典逻辑确实是关于真值的。
但线性逻辑选择了另一种立场:逻辑是在规范推断的合法性,而推断发生在一个有代价的世界里。不是每个"已知"都可以免费重复使用。如果你想用一个资源,你必须为它付出代价。这不是对经典逻辑的否定,而是它的推广——经典逻辑是线性逻辑加上"资源可以任意复制"这个特殊假设之后的退化版本。
16.2 线性逻辑的符号体系
去掉收缩,逻辑的结构就改变了。最明显的变化是:你不能再随意"合并"两个假设,因为它们的计数开始变得重要。
线性逻辑引入了一套与经典逻辑平行但含义不同的连接词。
乘法连接词
加法连接词
乘法析取 ⅋(par):线性逻辑的对偶连接词,对应乘法合取的否定形式。
加法析取
这四个连接词构成两组对称的配对,背后有一个深刻的对偶结构:乘法连接词是关于资源的并发存在,加法连接词是关于资源的可选择性。
为什么要有这么多连接词?
经典逻辑里只需要两个基本连接词(比如
16.3 感叹号:可以重复使用的资源
但如果所有假设都只能用一次,我们就无法谈论真正的知识——那些确实可以重复引用的事实。线性逻辑通过一个特殊符号解决这个问题:感叹号
形式地,
- 弱化:
( 可以被丢弃——你不需要用它) - 收缩:
( 可以被复制——你可以把它用两次) - 推广:如果
,那么 (从无限资源推出的结论,也是无限可用的)
用了感叹号之后,线性逻辑就能恢复对经典推理的模拟:如果所有假设都带上
兔狲教授评
很多人到这里会说"好的,
16.4 蕴含的含义变了
去掉收缩之后,蕴含
线性蕴含
这不是知识的传递,而是资源的转换。五元钱
相比之下,经典逻辑里的
这个区分揭示了"知识"和"资源"的本质差异:知识是
16.5 三个现实的对应
线性逻辑不是逻辑学家的纯思想实验。它精确地对应了三个重要的现实。
内存管理。 在程序设计里,内存是资源:分配一块内存,用完必须释放,否则就泄漏了。经典逻辑对内存的直觉是错的——它假设你"知道"一块内存,可以无限引用,但内存在被释放之后就不存在了。线性类型系统(如 Rust 的所有权系统)的核心,正是线性逻辑:每个变量恰好拥有一次,传递所有权就是消耗一份假设,借用就是使用
量子计算。 量子力学有一条著名的禁令:不可克隆定理。一个未知的量子态不能被完美复制。在逻辑语言里,这正是"不允许收缩"——量子比特是线性资源,不能从
并发协议。 两个进程之间的通信协议,可以被精确地编码为线性逻辑命题。协议的"一方发送,另一方接收"恰好是线性蕴含:资源从一侧转移到另一侧,不可复制,不可丢弃。会话类型(Session Types)正是这个对应关系的工程实现,它允许编译器在类型检查阶段验证协议的正确性——逻辑保证了协议不会死锁,不会遗漏消息。
Curry-Howard 对应的延伸
第14章提到,证明和程序在结构上是同构的(这个对应关系在第22章会正式展开)。线性逻辑把这个对应关系推进了一步:线性证明对应线性程序——每个变量恰好使用一次的程序。这不是意外的巧合,而是因为线性逻辑在设计上就是为了捕捉"计算是消耗资源的过程"这个直觉。Girard 最初发现线性逻辑,正是在分析 System F(多态 λ-演算)的证明结构时——他在逻辑的深处发现了资源的痕迹。
16.6 两种真值:占有与选择
线性逻辑还引出了一个更基本的哲学转变:"真"不再只有一种意思。
在经典逻辑里,
在线性逻辑里,"拥有
- 张量式拥有:
——你同时拥有 和 两个独立资源。 - 加法式拥有:
——你拥有两个选项,可以决定用哪一个。
这两种"拥有"在资源经济学里有完全不同的价值。我有一个苹果和一个梨,不等于我有"可以选苹果或梨"的能力——前者是两份资源,后者是一份资源加上一个选择权。
经典逻辑把这两种情况混为一谈,因为在真值层面,"
这个区分,将在第17章里以一种完全出乎意料的方式重新出现:当真值本身变成一个连续区间
16.7 过渡:在真值变成连续之前——三值逻辑作为热身
第17章要做一件激进的事:把真值从
波兰逻辑学家 Łukasiewicz 在1920年提出了三值逻辑。真值集合是
否定:
合取:
析取:
蕴含:
(蕴含的规则是三值逻辑里最反直觉的部分,多看几眼。当
练习: 用上面的规则,计算以下命题在所有三种真值赋值组合下的值。设
(逆否命题) (排中律)
第3个问题最有意思:在经典逻辑里,
排中律,在三值逻辑里,不再是重言式。
这不是一个技术细节。它意味着:当你把"真值"扩张,你就在隐含地修改你愿意接受的推断规则。三值逻辑拒绝排中律;经典逻辑接受它。这不是哪个更"正确",而是两种不同的选择,对应不同的推断合法性理解。
第17章要做的事,是把这个方向推到极致:真值不是三个点,而是
悬而未决
线性逻辑里的"真"是什么? 在经典逻辑里,语义很清楚:命题要么真要么假,真值表给出一切。在线性逻辑里,资源的"语义"是什么?如何定义一个模型,使得"
到什么程度,"资源"是一个精确的数学概念? 线性逻辑给了我们一个形式框架,但"资源"本身仍然是一个直觉词。什么是一份资源?资源的复制何时是合法的?这些问题在不同的应用域里有不同的回答,而线性逻辑提供的是一个可以容纳这些不同回答的框架,不是一个单一的答案。
去掉交换规则会怎样? 第14章列了三条结构规则:弱化、收缩、交换。线性逻辑去掉了收缩(有时也去掉弱化)。如果再去掉交换规则——让假设的顺序变得重要——就得到有序逻辑(Ordered Logic),它对应着有时间顺序的资源使用,比如消息必须按顺序发送和接收的通信协议。这个方向还在展开中。
如果"真"本身是一个程度,推断规则又该怎么改? 线性逻辑通过资源计数,把
思考题
★ 热身
用线性逻辑的资源直觉,判断以下推断是否成立。不需要写形式证明,只需要用"五元钱买咖啡"那类资源例子说清楚为什么对或错。
(同时拥有 和 ,可以单独推出 ) (拥有" 或 的选择权",可以推出 ) (无限可用的 ,可以取出一份 ) (一份 ,可以变成无限可用的 )
第1和第2的对比是这章最重要的区分。第4是收缩规则被拿走后直接失效的推断。
★★ 推导
考虑以下三个推断,判断在线性逻辑中是否成立,并写出理由。
(两份" 变为 "的能力,可以合并为一份?) (无限可用的转换规则,加上无限可用的输入,可以产生无限可用的输出?) (消耗 和 产生 ,等价于先消耗 、再消耗 产生 ?)
第1个涉及收缩。第2个涉及感叹号的推广规则。第3个是线性逻辑版本的柯里化——想想它在资源意义上是否真的成立。
★★★ 挑战
Rust语言的所有权系统实现了线性类型:每个值恰好拥有一个所有者,传递所有权就是消耗一份假设,借用(borrow)对应使用
试着用线性逻辑的符号,为以下Rust操作各写一个对应的线性逻辑推断式:
- 把变量
x的所有权移动给函数f(移动语义) - 把变量
x的不可变引用传给函数g(不可变借用) - 函数返回值(消耗输入,产生输出)
不要求完全精确,但要说清楚:哪些操作对应消耗性推断(线性蕴含
