一封给面向对象的情书
摘要
一篇论证面向对象编程的真正本质是消息传递而非类,并介绍了Abject项目和Ask协议,作为对这一思想在AI领域的复兴的文章。
<p><a href="https://lobste.rs/s/9mocvp/love_letter_object_orientation">评论</a></p>
查看缓存全文
缓存时间: 2026/07/24 21:06
# 一封写给面向对象的情书
来源:https://blog.mempko.com/a-love-letter-to-object-orientation/
对象因合理的原因而失宠,我们却扔掉了真正重要的部分。Abject 项目和 Ask 协议在 AI 时代将其带回了世界。
一封写给面向对象的情书Abject 项目截图## 面向对象编程的起源
我很难向人们解释我的 Abject (https://abject.world/?ref=blog.mempko.com) 项目的核心是什么。我一直在听取人们的反馈并更新网站,使其更清晰。人们难以理解的一个想法是,当我说“互联网是一个面向对象的系统”时。受过计算机编程训练的人常常难以理解这一点,因为我们被训练成认为面向对象意味着类和类层次结构。从这个角度来看,这是说不通的。
创造“面向对象编程”这个术语的人是 Alan Kay,当时他还在研究生阶段,在开发 Smalltalk 之前。他受到了 Ivan Sutherland 的 Sketchpad (https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-574.pdf?ref=blog.mempko.com) 的影响,这是最早包含面向对象思想的系统之一。Sketchpad 没有现代意义上的文本编程语言;它是可视化的,通过约束和求解器来解决问题。Kay 也受到他生物学背景的影响。
Alan Kay 的伟大想法是消息传递。当他开发 Smalltalk 时,内核是关于在对象之间发送消息,并拥有一个良好的元系统来实现晚期绑定。所谓晚期绑定,是指在消息被接收的那一刻才决定消息的含义,而不是预先将该决定固化在类层次结构中。所谓元系统,是指允许系统推理并安全地扩展这些规则的层面。这就是为什么 Kay 对对象的定义与互联网直接相关:互联网不是一个大类层次结构;而是独立的系统通过约定的边界发送消息。Kay 在 1998 年 Squeak 开发邮件列表 (https://lists.squeakfoundation.org/pipermail/squeak-dev/1998-October/017019.html?ref=blog.mempko.com) 的一条消息中建立了这种联系。我将在此处重现它,因为它非常重要。
> **Alan Kay** alank at wdi.disney.com (https://blog.mempko.com/cdn-cgi/l/email-protection#d3a0a2a6b6b2b8feb7b6a5f6e7e3bfbaa0a7a0fda0a2a6b6b2b8b5bca6bdb7b2a7babcbdfdbca1b4ec80a6b1b9b6b0a7eea3a1bca7bca7aaa3b6a0f6e1e3a5a0f6e1e3b0bfb2a0a0b6a0f6e1e3a4b2a0f6e092f6e1e381b6f6e092f6e1e380a6bdf6e1e4a0f6e1e39bbca780a3bca7f59abdfe81b6a3bfaafe87bceee2fde6fde7fde0e1fde2eaeaebe2e3e3eae1e1e7e5e7e7fde3e3b2b0e5b6eae3f6e7e3beb2babffdb5a1babafdb0bcbe) *1998年10月10日星期六 04:40:35 UTC*各位——我只是温和地提醒一下,我在上次 OOPSLA 会议上费了很大劲试图提醒大家,Smalltalk 不仅仅是它的语法或类库,它甚至不仅仅是关于类的。我很抱歉自己很久以前为这个话题创造了“对象”这个术语,因为它让许多人关注次要的想法。伟大的想法是“消息传递”——这才是 Smalltalk/Squeak 内核的全部意义所在(这也是我们在 Xerox PARC 阶段从未完全完成的事情)。日语中有一个小词——ma——表示“在两者之间的东西”——也许最接近的英语对应词是“interstitial”(空隙)。构建伟大且可扩展系统的关键,更多在于设计其模块如何通信,而不是它们内部的属性和行为应该是什么。想想互联网——为了生存,它(a)必须允许许多不同类型的想法和实现,这些想法超越任何单一标准,并且(b)必须允许这些想法之间不同级别的安全互操作性。如果你只关注消息传递——并意识到一个好的元系统可以对对象中使用的各种二级架构进行晚期绑定——那么这个线程中许多基于语言、UI 和操作系统的讨论实际上都是毫无意义的。这就是为什么我在上次 OOPSLA 会议上抱怨——尽管在 PARC 我们不断改变 Smalltalk,总是将其视为一个进行中的工作——但当 ST 进入更广阔的世界时,它几乎被视为“只是需要学习的东西”,就好像是 Pascal 或 Algol 一样。Smalltalk-80 从未真正变异成更好的 OOP 版本。考虑到当前编程的整体低水平,我认为这是一个真正的错误。我想我还指出过,不仅拥有一个完整的元系统至关重要,而且要有围栏来帮助守卫跨越元边界的操作。其中最简单的一个是我在六十年代后期最初涉足这一领域的动机之一:认识到赋值是函数的一个元级别变化,因此不应在同一层级处理——这是封装这类状态变化、不让它们随意发生的主要动机之一。我想说的是,一个允许在普通编程过程中执行其他元操作的系统(比如改变继承的含义,或者什么是实例)是一个糟糕的设计。(我相信系统应该允许这些事情,但设计应该使得在进行重大扩展时必须有清晰的围栏需要跨越。)我建议,如果聪明且有才华的 Squeak 列表成员更多地思考元编程的下一步应该是什么——我们如何能获得强大的能力、简洁性以及意义的安全性?向大家致敬,Alan
## Abject 导向编程
我将 Abject 项目称为一个 Abject 导向系统,这是对“Object-Oriented”这个短语的戏仿。当我谈论对象时,我指的是 Alan Kay 所说的那种:它实际上是关于消息传递,并且拥有一个执行晚期绑定的元系统。
## Ask 协议
Abject 系统中的关键消息是“ask”消息,它实际上是系统的心脏。它关乎实现 Ask 协议 (https://abject.world/ask-protocol/?ref=blog.mempko.com)。Abject 系统中的每个对象都实现了这个协议。我是说系统中的每一个对象。我如何构建 Abject 系统的细节不如这个协议重要。我构建它是为了测试终极元系统。
``
ask(question: string) -> string
``
这就是 ask 协议。就这么简单。
任何对象都可以用简单的英语问另一个对象:“我如何保存这个草图?”而存储对象可以用它接受的精确消息形状来回答。这个对象也可以向发送者回问问题。它可以是一个双向对话。在它们背后,你需要一个模型来将英语转换为代码,LLM 目前在这方面做得相当不错。这是一个为未来而设计的项目,为一个这类操作可以快速且本地化的世界而设计。今天,你可以使用来自 Anthropic、OpenAI 或 OpenRouter 等提供商的租用模型来运行 Abject,它也支持本地模型。租用路径现在很实用;本地路径是我希望计算发展的方向。
注册表帮助你发现对象以解决你的问题扩展对象的一个问题是,对象之间的所有交互,它们传递的消息,都必须由人们编码。这限制了这些系统的可组合性。如果你有两个对象使用不同的消息格式,你需要编写一个桥接器来连接它们。如果你想创建一个新对象来与另一个对象通信,必须首先了解另一个对象能处理哪些消息,并确保正确格式化它们。
Ask 协议解决了这个问题。因为我们现在的 LLM 擅长编写代码,尤其是在小规模下,它们可以自动构建这些桥接器。它们可以自动创建与现有对象通信的新对象。这个协议能够实现这一点,因为 LLM 可以与对象进行对话以发现它们如何工作。对象之间可以进行对话来改变自身。你不再需要手动将它们粘合在一起。
## 对话与压缩 vs 抽象
手动询问对象对于这一切,有一个反对意见我需要说明,因为一旦人们接受对象可以互相交谈,这是我最常听到的一个:如果系统外部有一个 LLM,它可以读取每个对象的清单,为什么对象还需要互相交谈?让观察者读取清单,弄清楚消息的格式,然后编写桥接它们的对象。一次读取,一次通过,完事。这听起来比一个协议更简单。我认为这是一个更糟糕的方案,原因有两个。
第一个是对话。当一个对象处理一个 ask 请求时,它知道是谁在问。这意味着它可以反问。它可以问:你实际上想在这里做什么?是美元还是美分?当这个失败时应该发生什么?这需要是原子性的吗?一个读取清单的外部观察者得不到这些信息。它必须猜测,而猜测的问题在于,它是悄无声息地发生的,并且生成的产物中没有记录猜测是在哪里做出的或确信程度如何。ask 协议将模糊的集成转变为协商集成。处理程序可以探究意图,反驳一个约束,拒绝它认为错误的请求,或者将调用者重定向到某个应该做这项工作的其他对象。猜测不是询问的较弱形式。它是一种不同的活动,并且没有附带误差条。
第二个原因与清单能承载的内容有关。清单及其文档对它们背后的实现只字不提,这可不是一个小疏忽。Trygve Reenskaug 和 James Coplien 多年来所做的 DCI 写作 (https://fulloo.info/?ref=blog.mempko.com) 有力地指出了这一点。在 fullOO 网站 (https://fulloo.info/?ref=blog.mempko.com) 上:“一个类告诉我们关于作为其实例的各个对象的所有属性。它没有告诉我们这些实例如何协同工作以实现系统行为。”更直白地说:“结果是我们的代码并不能揭示系统将如何运行的一切。”清单就是换了个名字的类接口。
在他们发表于 Artima (https://www.artima.com/articles/the-dci-architecture-a-new-vision-of-object-oriented-programming?ref=blog.mempko.com) 的 DCI 论文中,Reenskaug 和 Coplien 回溯到 Smalltalk 精确地命名了这一点:“在 Smalltalk 中,你可以作弊并调用对象继承层次结构中任何类的方法,无论默认实现是否出现在基类中。它会起作用,但这加剧了发现问题,因为基类接口并不能代表对象的全部行为。”发现问题。这正是外部观察者所面临的挑战的名称。接口不能代表行为,再仔细阅读也无法解决,因为缺失的部分从未被写下来。
所以这里有一个我认为没有被足够频繁地画出的区别:抽象和压缩不是同一个操作。抽象丢弃了实现。某人在某个时刻决定了什么重要,将其写下来,然后丢弃了其余部分。因为它在一个时刻被冻结,所以它过时了。当实现发生变化时,抽象要么泄露,要么悄悄说谎,契约在所有依赖它的人脚下发生了变化。仅凭清单工作的外部观察者,是在依据别人在更早的时候、为了一些其他目的而做出的关于什么重要的、冻结的、有损的判断来工作。
ask 处理程序则不是这样。它在对象内部运行,可以访问实际的实现代码。当它回答一个问题时,它不是背诵某人在某时刻写下的摘要;它是在根据实际的东西,实时地推导出一个。这是压缩而非抽象。它仍然有损,因为任何比程序短的答案都是如此,但它忠实于程序当前的行为,并且每次有人询问时都会按需重新推导。抽象丢弃并冻结。压缩保留并保持最新。这就是为什么一个对象能比任何最仔细阅读其清单的外部观察者告诉你更多关于它自身的信息。
这就是为什么我不认为 ask 协议是坐在清单之上的一个便利层。它是让系统能将一个问题路由到唯一一个答案实际存在的地方的那个东西。
## 理论与工件
作为模式存储的理论我经常回到 Peter Naur 1985 年的论文“编程作为理论构建”,其中的一个想法我认为是整个软件领域最重要但也是最被忽视的见解之一。简要来说,Naur 的论点是,程序不是程序员工作的主要产物,主要产物是一个理论。关于问题的理论:为什么它有这个形状,其中有哪些力量在紧张博弈,为什么这个特定结构能很好地解决这些力量而不仅仅是足够应付。代码是从该理论产生的一个工件,是它的影子,是某个特定时刻冻结的某些决策的记录。但代码不是理论,你也无法通过阅读代码来恢复理论。当持有理论的人离开时,这个程序在某种意义上就死了,即使它仍然可以编译和运行。
每当有人问我为什么 Abject 的结构如此,尤其是当我描述它如何处理对象时,我都会想到这一点。Abject 中的对象并不珍贵。系统创建它,给它一个注册名称,并分配一个运行时标识符,但运行时标识符是明确短暂的。当系统重写一个对象时,旧的运行时实例被孤立并进行垃圾回收;一个新的实例在相同的持久名称下取代它。当系统完全丢弃一个对象时,实现会在审计跟踪中被标记为已丢弃,并且名称被注销。记录中没有删除任何内容,但运行中的对象就不见了。从内部看,这就像一个系统将其自身的代码视为可消耗品,而事实确实如此。
系统能够如此草率的原因在于它同时在积累更持久的东西。当系统与你一起工作,构建对象,将它们连接起来,完成目标,观察什么成功什么失败时,它会写入一个知识库。知识库中的每个条目都有一个类型:事实、经验教训、见解、参考和模式。条目通过关键字、标签或精确标题被召回,并且每次新代理开始工作时,其中一部分条目会被注入到提示上下文中。这个知识库不是事后才写的文档;它是系统已经弄清楚的所有值得向前传递的东西的持续积累。当一个目标完成时,系统会从结果和任务历史中挖掘模式。那些候选模式等待晋升,对匹配器不可见,直到有人——你或系统本身——决定它们足够可靠可以信任。一旦晋升,一个模式会根据每个未来任务的上下文进行评分,如果匹配得足够好,就会被提交给将要执行该任务的代理。
这就是 Christopher Alexander 在提出模式语言构想时所想的那种机制——不是解决方案的清单,而是一个经过测试的解决方案的活词汇:每个模式都命名一个反复出现的张力、使其困难的力量,以及在这种上下文中解决这些力量的结构性举措。模式不会告诉你该做什么;它会告诉你哪种形状倾向于存活。James Coplien,我在整个文章中一直引用他的 DCI 著作,几十年来一直认为模式是软件中架构知识的真正单元。那就是你实际上正在构建的 w
相似文章
艾伦·凯谈"面向对象编程"的含义 (2003年)
2003年艾伦·凯的电子邮件阐明了"面向对象编程"的原始含义——以消息传递、数据隐藏和生物学/细胞隐喻为中心,最初有意省略了继承。
面向观察的编程:我如何围绕*观察*而非*执行*组织终端AI智能体集群
一位开发者详细介绍了观察导向编程(OOP),这是一种围绕观察可观测性数据而非任务执行来组织终端AI智能体群体的范式。该系统使用tmux、cron、bash和仅追加日志文件,智能体充当观察者,关联信号并仅在解释后才能具体化行动。
面向切面编程的回归
一篇重新审视面向切面编程的博客文章,讨论了程序员必须管理的众多横切关注点,如正确性、日志记录和安全性。
@PandaTalk8: 几年前, 遇到一个文科生的神人。 他问我面向对象是什么, 我说是一种抽象,给他讲了一大堆, 类, 继承,多态之类的概念。 最后他若有所思, 原来你说的就是分类啊。 他这个回复把震惊了,原来一直都没有搞懂真相的是我, 事实上还真的就是分类的…
A tweet shares a personal anecdote about explaining object-oriented programming to a humanities student, who insightfully summarized it as 'classification', leading the author to discover applied category theory and a related course.
@mvanhorn: https://x.com/mvanhorn/status/2063865685558903149
本文解释了AI编程中'循环'的概念,即开发者编写程序来提示编码代理,而不是手动提示,这一概念由Peter Steinberger和Boris Cherny推广开来,并讨论了这种转变如何代表了AI辅助开发中的新抽象层。