前言
关于软件工程
我对软件工程的底层设计存在一些根本性怀疑。
现代编程语言以空间隐喻——内存、状态、对象图——为底层语法,成功解决了图灵机层面的计算表达问题。但时间维度,也就是因果、顺序、生命周期、并发、一致性,并未被同等对待。它被转译为空间操作:锁、GC、事务。这些时间问题的复杂度被层层上抛,最终落在了应用层。
这种"用空间语法解决时间问题"的根本性错配,是否才是软件工程复杂度的真正根源?
冯·诺依曼架构的空间隐喻
冯·诺依曼架构的核心是内存、状态、指令和状态变迁。内存是空间,数据存储在地址里,程序是对空间的读写操作。这本质上是一种空间隐喻。
更深一层,冯·诺依曼架构把指令和数据统一编码为可寻址存储器中的内容:指令隐喻了时间,数据隐喻了空间。编程中的复杂度由此被层层转嫁——冯·诺依曼架构抛给操作系统,操作系统又抛给编程语言——最终造成的后果就是:空间耦合了时间。
以 GC 为例。GC 算法本质上是通过 Root → 对象图 判断哪些对象可达:可达保留,不可达删除。这是一种典型的空间推断时间。但如果我们换一种思维呢?一个数据在时间上何时可以消失——例如 Event Sourcing、事件驱动架构所展示的那样——以因果模型为基础建模,整个系统就会呈现为事件之间的流程变化。
更本质地说,我们一直在用"状态 + 状态转换"替代"事件 + 因果历史",然后把复杂度层层上抛,最终抛到高级编程语言层面,表现出来的形式就是面向对象。
面向对象把状态(空间)和行为(时间)打包在一起。每个对象都是一个小型的、拥有独立时间线的自包含状态机。系统的状态空间因此指数级爆炸,而统一的因果叙事被切碎在无数对象的私有空间里。
我认为,"时间"在现代编程模型中被严重低估了。时间不应该只是一个物理刻度,它本身可以被看作一种能够被形式化的底层基础数据结构。事件不能仅仅被装在时间里;相反,事件之间的关系就是时间本身。时间可以表现为一种因果模型,而因果模型又可以视作一个动态的图传播网络——A 导致 B,B 导致 C,这种最简单的串行因果关系,就是其中的一种。
从更广义的角度说,软件工程的本质就是现实世界的投影。时间和空间在数学与物理性质上本就存在根本差异,必须分开处理。因果必须显式化,时间和空间必须并列为一等公民。
面向因果
因此,一个系统运行时,必须清晰划分出两种不同的事物:
- 因果运行时:负责解决时间中的冲突——竞争、顺序、传播、生命周期、一致性——并建立起唯一历史,代表着时间上的隐喻。
- 感知运行时:让所有空间组件无竞态地并行展开——规模、组织、嵌套、渲染、资源管理——负责在空间中展开已经确定的历史,代表着空间上的隐喻。
这便是面向因果的核心论断。