第 18 章
括号之外的遗产
在中国大陆与台湾地区的政治对话史上,有一个表述构造得极为奇特。它不是一份签署的文件,不是一项条约,甚至不是一份共同起草的声明。它只是双方各自以口头方式表达了对一个中国原则的坚持,然后由第三方将这两种表述概括为一种共识。这个后来被称为“九二共识”的构造,其内容从未被双方共同书写在一张纸上,但它的存在却成为此后十五年两岸关系不可动摇的政治基础。共识的力量不在于它被记录在什么媒介上,而在于它被多少人视为理所当然。那些被简化掉的微妙之处去了哪里?在政治领域,它们被悬置在共识的模糊性中;在编程语言的世界里,它们则沉淀为一种无声的遗产。
在编程语言的世界里,Clojure留下了一种类似的遗产。这门语言从未成为主流,从未出现在TIOBE排行榜的前二十名,从未有过大厂的全栈背书。但到了2010年代后期,一些原本属于Clojure社区日常实践的概念——不可变数据结构的系统化使用、函数式组合的工程方法、REPL驱动开发的工艺——已经开始出现在JavaScript、Python和Java的主流实践中。这些概念并非Clojure首创,但Clojure社区将它们锤炼成了一套连贯的日常开发习惯,而这种习惯通过会议演讲、博客文章和跨语言的开源项目,逐渐渗透到了更广阔的编程世界。没有人签署过一份名为“共识”的文件,但那种默契已经在那里了。
追溯这种渗透的路径,需要从2013年说起。那一年,Facebook发布了React。这个用于构建用户界面的JavaScript库在当时的前端圈子里引起了不小的争议——它最令人困惑的特性之一,是它坚持不可变性的重要性。React的文档建议开发者不要直接修改状态对象,而应该用新的对象来替换旧对象。这个理念在JavaScript社区中远非主流。JavaScript的对象从语言诞生之初就是可变的,大多数开发者习惯于在任何地方直接修改数组或对象的属性。React团队并不是Clojure社区的人,但他们的技术决策背后有一条可以追溯的线索。
React的核心开发者之一Pete Hunt在2013年的一次演讲中解释为什么要使用不可变数据。他给出的理由——通过比较前后状态的引用来判断是否需要重新渲染,从而获得显著的性能优势——几乎可以直接追溯到Rich Hickey在2008年的一次演讲中的论述。Hickey在那次演讲中阐述了一个概念:状态不是随时间变化的值,而是随时间变化的一系列值。每个值本身是不可变的,变化发生在从一个值到另一个值的过渡中,而不是发生在值内部。
这个观念在Clojure社区中通过无数次的REPL会话、代码审查和会议讨论被反复锤炼,最终凝结成了一种日常实践中的直觉。Hunt在演讲中并没有提到Clojure,他谈论的是React的组件模型和虚拟DOM的diff算法。但那个论证的结构——将不可变性从一种哲学立场转化为一种工程实践中的具体优势——与Clojure社区多年来在邮件列表和会议演讲中反复阐述的内容惊人地一致。
这不是巧合,也不是直接的技术移植。React团队中的一些成员曾经接触过函数式编程语言,包括Clojure和Haskell,他们将那种思维模式带入了JavaScript的生态系统。但更关键的是,那些在Clojure社区中被反复讨论、验证和提炼的论证,已经成为了一种可传播的知识形式,可以在脱离原始语境的情况下被其他社区吸收和重新表述。
2014年,Immutable.js发布了。这个同样由Facebook开发的JavaScript库提供了持久化不可变数据结构,包括Clojure社区熟悉的向量、映射和集合。它的文档中明确提到了Clojure和ClojureScript作为灵感来源。
但更重要的是,Immutable.js的设计选择——将不可变数据结构作为一个独立的库提供,而不是改变语言本身——反映出了一种对实践的关注。在Clojure中,不可变性不是通过语言级别的强制规定来实现的,而是通过提供一组高效的数据结构,让开发者在使用中自然地体会到不可变性的优势。Immutable.js采用了同样的策略。
这个库在JavaScript社区中的接受过程本身就是一个值得观察的现象。最初,许多开发者对不可变数据结构持怀疑态度。JavaScript的语法和标准库都是围绕可变操作设计的,使用不可变数据结构意味着需要学习一套新的API,改变许多日常编码习惯。
但随着时间的推移,越来越多的项目开始采用Immutable.js,尤其是在与React配合使用的场景中。开发者们发现,不可变数据结构解决了一些实际的问题:状态管理的可预测性、变更追踪的效率、并发操作的简化。这些发现不是通过抽象的说服,而是通过具体的实践——就像Clojure社区中那些开发者在REPL中试验、在邮件列表中分享、在会议演讲中演示的那样。
到了2016年,Redux——一个受Flux架构和函数式编程影响的JavaScript状态管理库——已经成为React生态系统中事实上的标准。Redux的核心原则之一是使用纯函数来处理状态变更,并且依赖不可变性来进行变更检测。它的创建者Dan Abramov在2015年的一次演讲中,用了将近一半的时间来解释为什么不可变性是重要的。他给出的理由——可预测性、可测试性、时间旅行调试——几乎每一个都可以在Clojure社区的讨论中找到前身。
Abramov本人并没有Clojure背景,但他所阐述的那些论证,已经在Clojure社区的会议演讲和博客文章中被反复打磨了多年。这种影响的匿名性恰恰是它的特征。一个JavaScript开发者使用Immutable.js或Redux时,不需要知道Clojure的存在。
他只需要知道不可变数据结构能够解决他面临的问题。但那个“知道”本身——将不可变性视为一种可用的解决方案,而不是一个抽象的学术概念——依赖于一个已经形成的知识生态。在这个生态中,不可变数据结构不再是一个函数式编程语言中的特殊特性,而是一种可以在任何语言中使用的工具。这种观念的形成,Clojure社区贡献了远超其体量的份额。
函数式组合的渗透遵循着类似的路径。2014年,Java 8发布了。这个版本引入了Lambda表达式和Stream API,使得Java开发者能够以一种更接近函数式风格的方式来处理集合。Java 8的设计团队在做出这些决定时,参考了多种函数式编程语言的经验,包括Clojure。
但更重要的是,Java社区在吸收这些特性时所依赖的教学材料和最佳实践,有一部分来自Clojure社区的积累。Rich Hickey在2012年的一次演讲中讨论了如何设计良好的函数式API。他提出了一个原则:函数应该接收简单的东西,返回简单的东西。这个看似简单的原则深刻地影响了Clojure核心库的设计。
在Clojure中,函数倾向于接收不可变的数据结构并返回新的数据结构,而不是修改传入的对象。这种模式在Java 8的Stream API中得到了重现:Stream上的操作不会修改原始集合,而是产生新的Stream。设计者不一定直接引用了Hickey的演讲,但那种设计哲学——通过不可变转换来组合操作——已经通过Clojure社区的实践和传播,变成了函数式编程常识的一部分。
2015年,Python社区也开始更认真地讨论函数式编程。Python 3.5引入了类型提示,虽然这与函数式编程没有直接关系,但它开启了一场关于Python编程风格的更广泛讨论。在这场讨论中,一些开发者开始主张更广泛地使用不可变数据结构和纯函数。
Python的标准库中包含了一些函数式编程工具——map、filter、functools模块——但这些工具长期以来处于边缘地位。到了2010年代后期,越来越多的Python项目开始将函数式编程作为一种核心设计原则。
这个转变的部分原因是数据科学和机器学习领域的崛起。在这些领域中,不可变性——数据在转换过程中不被修改——成为一个实际的需求,而不仅仅是一种哲学偏好。NumPy和Pandas等库的设计中包含了不可变性的概念,虽然它们的实现方式与Clojure不同。
但更重要的是,那些在数据科学社区中教学的函数式编程模式,其中有一部分可以追溯到Clojure社区的教学实践。Clojure社区在2010年代初期发展出了一套教学方法,将函数式编程从抽象的数学概念转化为具体的日常实践——如何用map和reduce替代循环,如何用不可变数据来组织程序状态,如何通过函数组合来构建复杂的转换。这些教学方法通过博客文章、会议演讲和在线教程传播,最终渗透到了Python的数据科学教学中。
REPL驱动开发的扩散则呈现出不同的模式。在Clojure社区中,REPL不仅仅是一个用于测试代码片段的工具,它是开发过程的核心。开发者习惯在REPL中逐步构建程序,将一段代码反复试验,立即看到结果,然后逐步组合成更大的功能。这种开发方式在Clojure社区中是如此自然,以至于许多开发者很难想象没有REPL的编程。
但REPL驱动开发并不是Clojure的发明。Lisp方言从诞生之初就包含了REPL的概念,Python和Ruby等语言也提供了交互式解释器。
区别在于,在Clojure社区中,REPL的使用被提升到了一种手艺的高度。社区发展出了一套关于如何有效地使用REPL的实践知识:如何将编辑器连接到运行中的REPL进程,如何在REPL中探索未知的库,如何将REPL中的试验转录为测试用例,如何通过REPL会话来向他人展示代码的行为。这些实践知识不是通过正式的文档传播的,而是通过会议上的现场演示、配对编程和屏幕录像传递的。
2010年代后期,这种开发方式开始在其他语言社区中出现。2017年,Visual Studio Code引入了对Jupyter Notebook的支持,使得Python开发者能够在编辑器中直接运行代码片段并查看结果。2018年,Quokka.js为JavaScript开发者提供了类似REPL的即时反馈体验。2019年,Java 9引入了JShell,一个交互式的Java REPL。
这些工具的出现,部分是对开发者需求变化的回应——开发者越来越期望能够立即看到代码的结果,而不是等待完整的编译和运行周期。但那个需求本身,那些关于即时反馈价值的论证,有一部分来自Clojure社区多年来的实践和传播。Clojure社区在REPL驱动开发上的影响力,不在于它创造了这个工具,而在于它展示了这个工具可以如何使用。
在2010年代初期,当许多语言的开发者仍然将REPL视为一个用于学习语言或测试小段代码的辅助工具时,Clojure社区的开发者已经在用REPL构建整个应用程序了。他们通过会议演讲和屏幕录像展示了这种工作方式——用REPL来探索问题空间,逐步构建解决方案,然后将其转化为可维护的代码。这些演示不是关于Clojure语法的,而是关于一种开发方法的。其他语言的开发者看了这些演示后,开始在自己的语言中寻找类似的体验。
这种跨语言的影响有一个值得注意的特征:它几乎从来不是通过直接的复制实现的。JavaScript社区没有照搬Clojure的REPL,而是发展出了自己的工具——Quokka、CodeSandbox、浏览器中的即时重载。Python社区没有使用Clojure的nREPL协议,而是围绕Jupyter Notebook构建了自己的交互式开发生态。Java的JShell在功能上比Clojure的REPL更简单,但它满足了Java开发者在一个不同场景下的需求。每种语言都在自己的生态系统中找到了实现即时反馈的方式,但即时反馈是重要的这个前提本身,已经通过Clojure社区的示范而变得不言自明。
这种影响方式与Clojure社区自身的治理逻辑一脉相承。在Clojure社区内部,共识不是通过投票或中心化的决策形成的,而是通过示范和模仿逐渐沉淀的。一个开发者展示了某种使用REPL的方式,其他开发者觉得好用,就采用了。一个库的命名方式被证明是有效的,其他库就效仿了。代码的视觉秩序——括号的排法、缩进的宽度、空行的留白——不是通过正式的风格指南强制执行的,而是通过无数次的代码审查和日常模仿形成的。
这种“隐性宪法”在社区内部运作的方式,与Clojure对外部世界产生影响的方式遵循着同样的逻辑。外部社区没有签署一份文件,没有接受Clojure社区的权威。他们只是看到了某种实践,觉得它有效,就在自己的语境中采用了它。
这种采用不一定伴随着明确的致谢或引用。一个JavaScript开发者可能从未听说过Clojure,但他在使用Immutable.js时采用的那种设计模式,在结构上复制了Clojure社区多年前在邮件列表中讨论过的方案。一个Python开发者可能从未读过Rich Hickey的演讲,但她在教学map和filter时所使用的那些例子,与Clojure社区的教学材料惊人地相似。这种影响是匿名的、弥散的,但却是真实的。
到了2020年代,不可变数据结构、函数式组合和REPL驱动开发这三个概念在主流编程语言中的地位已经发生了根本性的变化。它们不再是函数式编程语言的特殊特性,而是现代编程实践的基本组成部分。JavaScript的ES6引入了const关键字和扩展运算符,使得不可变操作更加方便。Python的dataclasses模块支持frozen参数,允许创建不可变的数据对象。Java的Records提供了不可变数据的一种简洁语法。Rust将不可变性作为默认行为。这些语言特性本身并不直接来自Clojure,但它们所满足的需求——对状态管理的可预测性、对并发操作的安全性的追求——是Clojure社区在多年实践中反复论证和传播的。
更重要的是,关于这些特性的论证方式已经改变了。在2008年,当Rich Hickey在演讲中阐述不可变性的价值时,他需要从第一原理开始构建论证。他需要解释为什么可变状态是有问题的,为什么不可变性可以解决这些问题,以及如何在实际编程中实现这些理念。到了2020年,这些论证已经成为编程常识的一部分。
一个面试初级开发者的工程师可以简单地说“我们使用不可变数据结构来避免状态管理的混乱”,而不需要从头解释为什么。那种解释的负担已经被转移了——被那些年复一年的会议演讲、博客文章和开源项目承担了。
这种转变的一个具体体现可以在Python社区中观察到。Python的赋值语句采用中缀记号的等号,被用来将名字绑定到值,以及用来修改可变对象的特性。Python支持增广赋值语句,将一个二元运算和一个赋值语句合并成一个单一语句。Python还支持序列解包,在等号左侧可以是一个表达式列表,右侧相应的是一个可迭代对象。这些语法特性本身是中性的,它们可以被用于可变或不可变的编程风格。但在2010年代后期,越来越多的Python教学材料开始强调不可变性的重要性,推荐使用元组而不是列表来存储不应修改的数据,使用functools模块中的工具来构建函数式管道。这些推荐背后的论证,与Clojure社区多年前阐述的内容一脉相承。
这种影响的匿名性引发了一个问题:如果没有人知道这些观念来自Clojure,那么它们还能被称为Clojure的遗产吗?这个问题触及了遗产概念本身。遗产不一定是被明确标注和致谢的。
遗产是一种残留——一种被继承但可能不被意识到其来源的东西。当建筑师使用某种结构技术时,他可能不知道这种技术最早是由谁发明的,但那种技术仍然构成了他工作的一部分基础。当程序员默认使用不可变数据结构时,他可能不知道Clojure社区在推广这种实践中扮演的角色,但那个实践仍然是他工具箱中的一部分。
Clojure社区对这种匿名性抱有一种复杂的态度。在邮件列表和会议讨论中,偶尔会出现关于Clojure为什么没有被更广泛地认可的讨论。一些社区成员认为,Clojure的贡献被低估了,那些主流语言引入的特性应该更多地归功于Clojure的影响。另一些成员则认为,这种匿名性恰恰证明了影响是真实的——如果观念已经渗透到不需要标注来源的地步,那么它已经成为了常识的一部分。这种分歧本身反映出了社区对自身遗产的不同理解。
从外部观察者的角度来看,Clojure对外部世界的影响可以被看作是一种共识的扩散。没有一份文件明确记录了Clojure社区与其他语言社区之间的技术共识。没有人签署过一项协议,承认不可变数据结构是好的实践。但那种共识已经在那里了,体现在无数开发者的日常决策中,体现在那些被主流语言采纳的特性中,体现在那些教学材料中反复出现的论证中。
这种共识的形成过程与Clojure社区内部共识的形成过程遵循着相同的模式。在社区内部,共识不是通过投票或中心化决策形成的,而是通过示范和模仿逐渐沉淀的。一个开发者在邮件列表中分享了一种使用方式,其他人觉得有效就采用了。一个库的命名方式被证明是好的,其他库就效仿了。
这种模式在外部影响中得到了重现:一个开发者看到了Clojure风格的不可变数据结构的优势,在自己的项目中实现了类似的东西,然后其他人看到了他的实现,觉得有效,就采用了。整个链条中没有中心化的推广,没有正式的协议,只有示范和模仿的缓慢扩散。
这种扩散的一个关键节点是跨语言开源项目的出现。2014年发布的Immutable.js是一个明显的例子,但还有其他项目。2015年,Facebook发布了GraphQL,它的类型系统和查询语言在结构上呼应了Clojure社区的EDN和Datomic查询模型。2016年,Elm——一个受Haskell和Clojure影响的函数式前端语言——开始影响JavaScript社区对状态管理和不可变性的讨论。这些项目不是Clojure的直接衍生品,但它们的设计决策中包含了那些在Clojure社区中被反复锤炼的论证。它们充当了观念传播的中继站,将Clojure社区中形成的实践翻译成了其他语言开发者能够理解的形式。
到了2018年,这种传播已经达到了一个程度,使得一些最初只在Clojure社区中流行的概念开始出现在主流编程教学的核心内容中。大学计算机科学课程中开始包含更多关于不可变性和函数式编程的内容。在线编程教程中,map、filter和reduce成为了标准工具。这种转变不是一夜之间发生的,而是多年积累的结果。
Clojure社区在这个过程中扮演的角色不是发明者——这些概念在学术计算机科学中有着更早的历史——而是翻译者和实践者,将抽象的概念转化为日常的编程习惯。这种转化工作本身是值得注意的。将函数式编程从学术论文转化为工业实践,需要的不仅仅是技术实现,还需要一套教学方法和论证策略。
Clojure社区在这方面投入了大量的精力,从2008年邮件列表中的早期讨论,到2010年代初期会议演讲的成熟,再到2010年代后期教学材料的丰富——这些投入构成了一个知识传播的基础设施。这个基础设施的产出不是关于Clojure语法的教学,而是关于一种编程思维方式的传播。那些从未写过一行Clojure代码的开发者,可能已经吸收了这种思维方式的一部分。
2020年,JavaScript的Optional Chaining和Nullish Coalescing操作符被广泛采用。这些特性与Clojure的some->和or宏在功能上相似,虽然影响链条不是直接的。但更重要的是,JavaScript社区在接受这些特性时所依赖的论证框架——如何安全地处理可能为空的值,如何通过组合来构建复杂的操作——已经包含了那些在Clojure社区中被反复讨论的内容。那个论证框架不再需要被重新发明,它已经在那里了。
这种影响的一个悖论是,它使得Clojure本身变得不那么必要了。如果一个开发者可以在JavaScript或Python中获得不可变数据结构的优势,那么他为什么还要学习Clojure呢?
这个问题在Clojure社区内部被反复提出。社区的一些成员认为,Clojure提供的不仅仅是独立的特性,而是一套连贯的设计哲学,这套哲学在没有括号的语言中难以完全实现。另一些成员则认为,Clojure的核心价值不在于它的语法,而在于它的思维方式,而这种思维方式可以在任何语言中实践。
这个分歧本身就是Clojure遗产问题的一个缩影。无论如何,到2024年,不可变数据结构和函数式编程已经成为主流编程实践的一部分。
这个事实本身就是Clojure社区遗产的一个证据。这个遗产不是通过中心化的推广实现的,而是通过示范和共识的缓慢扩散。它没有被记录在任何一份签署的文件中,但它存在于那些被改变了的、关于编程应该是什么样的日常判断里。
这些日常判断的累积效应是深远的。一个团队在2024年选择使用不可变数据结构时,不需要为这个决定辩护。一个代码审查者指出某段代码应该使用纯函数而不是修改全局状态时,不需要解释为什么。一个教学者推荐使用map和filter替代循环时,不需要从第一原理开始论证。这些判断已经成为编程常识的一部分,而常识的形成是无数个体在无数时刻做出的微小贡献的累积结果。
Clojure社区——尽管规模不大,尽管从未成为主流——在这个累积过程中扮演了超出其体量的角色。这种角色的一个具体表现可以在2019年的一次JavaScript社区调查中看到。
在那次调查中,当被问及为什么选择使用不可变数据结构时,受访者给出的理由中包含了更可预测的状态管理、更容易调试、更好的性能等条目。这些理由的表述方式与Clojure社区在2010年代初期邮件列表中讨论的内容高度一致。这种一致性不是偶然的,而是多年传播的结果。那些理由在被写出之前,已经在Clojure社区中被反复讨论、验证和精炼了。它们通过会议演讲、博客文章和开源项目传播到了JavaScript社区,然后被吸收和重新表述为JavaScript开发者自己的语言。
到了2022年,这种传播已经达到了一个程度,使得追溯影响的源头变得困难。一个Rust开发者使用不可变数据结构和模式匹配时,他的实践可能受到Haskell的影响,也可能受到Clojure的影响,更可能是受到各种来源的综合影响。当某个概念已经成为常识时,追问它的来源就变得不那么重要了。但那个事实本身——这个概念已经成为常识——是那些来源共同作用的结果。Clojure社区在这个共同作用中占据了一个位置,这个位置虽然不显眼,但却是真实的。
这种真实性的一个侧面是Clojure社区对函数式编程教学法的贡献。在2010年代初期,Clojure社区发展出了一套将函数式编程从抽象概念转化为具体实践的教学方法。这套方法强调在REPL中的即时反馈,强调通过小步骤构建理解,强调将复杂问题分解为可组合的函数。这些教学方法最初是为Clojure开发者设计的,但它们通过博客文章和会议演讲传播后,被其他社区的教学者吸收和改编。到了2020年代,Python教学中的一些常用例子——用map和filter处理列表,用reduce计算累积值——在结构上复制了Clojure社区多年来的教学材料。这种复制的匿名性不削弱它的真实性。
回到本章开头提到的那个构造。九二共识的精妙之处在于,它不需要共同签署一份文件,只需要各自口头表述后形成默契。这种默契的约束力不来自法律条文,而来自对违反后果的集体预期。在编程语言的世界里,类似的事情发生了。Clojure社区与外部编程世界之间没有签署过一份文件,没有达成过一项正式的协议。但那种默契已经存在了——关于不可变数据结构是好的,关于函数式组合是强大的,关于即时反馈是重要的。这些默契不需要被写下来,因为它们已经体现在了那些被广泛使用的工具、库和教学材料中。
这种默契的形成是Clojure社区的真正遗产。这个遗产不在括号之内——不在Clojure的语法、标准库或生态系统中——而在括号之外,在那些被悄悄改变了的关于编程的日常判断中。一个JavaScript开发者在使用Immutable.js时,一个Python开发者在教学map和filter时,一个Java开发者在设计不可变的数据类时,他们的决策中已经包含了那些曾经在Clojure邮件列表中被反复讨论、在Clojure会议中被反复演示、在Clojure代码中被反复实践过的论证。他们可能不知道这些论证的来源,但那不影响这些论证已经成为了他们工作的一部分。
这个遗产的匿名性恰恰是它最深刻的特征。那些被明确标注和致谢的影响往往停留在表面。
真正深刻的影响是那些已经被吸收到不需要标注来源的程度的影响。当一个程序员默认地认为不可变数据结构是好的实践时,那个默认不是他自己发明的,而是他所在的知识生态为他提供的。那个知识生态的形成是无数社区和个体贡献的累积结果。
Clojure社区在这个累积过程中做出的贡献,虽然无法被精确量化,但却是真实的。这种真实性的最终证据不在于统计数据或引用次数,而在于实践本身。
2024年,一个开发者开始一个新项目时,他选择的技术栈可能包含TypeScript、React和某个不可变数据工具库。他可能从未写过一行Clojure代码,但他的日常编程实践中已经包含了那些在Clojure社区中被锤炼了十五年的概念。他的代码是JavaScript的,但他的思维方式中有一小部分是通过一个复杂的传播链条从Clojure社区那里继承来的。这个继承不是他选择的,而是他所在的技术文化为他提供的。这种提供——这种将概念转化为常识、将论证转化为默认、将实践转化为习惯的过程——就是Clojure社区在括号之外留下的遗产。
然而,这种遗产有一个根本性的脆弱之处。它依赖于传播链条的连续性,而传播链条的每一环——从Clojure邮件列表的讨论,到会议演讲的阐释,到博客文章的转述,到其他语言社区的吸收和改写——都可能因为任何一环的断裂而失去连贯性。
某些论证在传播过程中被简化了,某些哲学前提在翻译中被丢失了,某些实践细节在模仿中被变形了。当教学者将Clojure的哲学转译为教学材料时,当其他语言的开发者将这些材料再转译为自己的实践时,每一步都伴随着信息损失。
到了2024年,那些被主流社区吸收的“不可变性”实践与Clojure社区原本锤炼出的那种日常习惯之间已经出现了明显的差异。不可变数据结构被广泛使用,但使用它们的方式——是作为语言特性而被动接受,还是作为设计哲学而主动选择——已经不同了。这种差异不会被标注在任何文档中,但它存在于那些决策的理据里,存在于那些被跳过的论证里,存在于那些被遗忘的前提里。
这就是Clojure社区在括号之外留下的遗产的真实面貌:它改变了世界,但改变的方式不能被精确地追溯,改变的结果不能被完全地辨识,改变的意义不能被最终地确定。只剩下那些被悄悄改变了的日常判断,在无数开发者的指尖延续着一种未被命名的传承。