第 12 章

语法高亮之后

把时间拨回2015年。那一年,Python社区在3.5版本中正式引入渐进类型系统。这个决定本身并不令人意外——早在2006年,吉多·范罗苏姆就已经在邮件列表里讨论过类型注解的可能性,当时社区的回应冷淡而谨慎。但到了2015年,这个想法终于落地,被写进了语法。类型注解不是强制性的。开发者可以声明类型,也可以完全不写,同一个项目里两种风格并存,解释器不做校验。这些注解出现在编辑器里,被高亮成不同的颜色,被补全引擎读取,被静态检查工具分析,却不会在运行时产生任何效果。它们是一种纯粹为阅读和编辑而存在的视觉层。

这件事和Clojure的关系,表面上看是零。Python强调可读性,以简洁语法著称。Clojure是一门Lisp方言,以括号和不可变数据结构为标志。二者分属不同的语言谱系,面对不同的开发者群体,解决不同的问题。但如果我们把视线从语言特性本身移开,转向开发者每天打开编辑器时看到的那个界面,一个更深层的联系就会浮现。

Python引入渐进类型的决定,实际上是在重新定义阅读代码时的视觉经验。在过去,你只需要读懂语法和逻辑。现在,你还需要读懂类型注解,而那些注解本身并不是代码运行所必需的。它们是一种视觉语法,叠加在代码文本之上,为编辑器工具提供结构化的信息,却始终停留在语言规范的边界之外。

语法高亮只是编辑器里最浅的一层改变。当Clojure的语言审美沉淀到开发者每天打开的开发环境中,它从一种可选的装饰变成一种习惯性的判断——这个转变是如何发生的?编辑器与IDE插件不再是简单的外围工具,它们把项目文件、代码排版和错误信息都收拢到同一个手边界面里。在这个界面里,一门语言不再是抽象的文法规则,而是变成了颜色、形状、快捷键和自动补全的反馈。开发者通过这个界面触摸语言,也通过这个界面辨认彼此。Clojure社区面对这个问题的起点,与Python截然不同。

里奇·希基在设计Clojure时明确选择了动态类型,并且把重点放在了不可变数据结构和显式的时间进展构造上。这不是疏漏,而是取舍。Clojure的哲学是:程序的状态变化应该通过少数几个明确定义的引用类型来管理,其余的一切都应该是不可变的值。在这个框架下,类型注解不会带来多少好处,反而可能削弱语言的灵活性。

但编辑器环境并不关心语言哲学。它只关心能不能给用户提供更好的补全、更快的跳转、更准确的错误提示。而这些功能,恰恰是静态类型系统的强项。

于是,Clojure社区面对着一个两难的处境。一方面是语言哲学的一致性:不加类型,保持简单,信任REPL的即时反馈。另一方面是编辑器体验的压力:越来越多的开发者在选择工具时,会把智能补全和错误提示的质量当作首要考量。不做类型系统,编辑器就难以提供这些功能。

编辑器提供不了这些功能,开发者就可能转向别的语言——不是因为他们不喜欢Clojure的语法,而是因为他们在每天八小时的工作中,需要一个更顺手的操作环境。这个两难并不是突然出现的。早在2010年前后,Clojure社区的早期开发者们就已经开始构建自己的编辑器工具。

最早的一批插件是为Emacs写的,因为Emacs本身就是Lisp的传统宿主,Clojure的早期用户几乎都是Emacs的使用者。那时的工具还很简陋:语法高亮靠正则表达式实现,括号匹配是Emacs自带的,REPL集成通过一个叫inferior-lisp的模式完成。这些工具的功能不强,但使用它们的人并不觉得缺少什么。

因为那个时期的Clojure开发者群体很小,大家彼此认识,遇到问题可以直接在邮件列表里问,或者看别人的代码。工具简陋,但社区紧密,知识传递的路径很短。转折发生在2012年到2014年之间。

那几年,Clojure的用户数量快速增长,Clojure/conj大会的规模扩大,企业开始在实际项目中使用Clojure。新来的开发者不再全部来自Emacs阵营。他们中有用IntelliJ的Java开发者,有用Sublime Text的Web开发者,还有用Vim的系统管理员。这些人进入Clojure社区时,带着自己的编辑习惯和工具偏好。他们希望在自己熟悉的编辑器里获得与Emacs相当甚至更好的体验。这就催生了一批新的编辑器插件:Cursive为IntelliJ提供了完整的Clojure支持,vim-fireplace让Vim用户也能方便地连接REPL,Light Table则试图重新定义编辑器与REPL的关系。这些插件的出现,解决了工具多样性的问题,但也带来了新的复杂性。不同的编辑器对Clojure的理解方式不同,因此同一个代码文件在不同编辑器里可能呈现出不同的面貌。

缩进宽度不一致,括号对齐方式有差异,错误信息的显示位置也不一样。更重要的是,这些插件各自实现了不同层次的语义理解。有的插件只是简单地高亮关键字和括号,有的则试图解析整个项目的依赖关系,提供跨文件的跳转和补全。这种差异意味着,一个开发者对Clojure的理解,往往取决于他使用的编辑器理解到什么程度。

这里出现了一个微妙的反转。在早期,工具是人的延伸,人通过工具完成工作。但到了这个阶段,工具开始反过来定义人能看见什么。一个使用基础高亮插件的开发者,看到的只是一个带颜色的文本文件。一个使用全功能IDE的开发者,看到的则是一个结构化的项目,每个符号都有来处和去处,每个错误都有位置和上下文。这两个人虽然都在写Clojure,但他们体验到的这门语言是不同的。语言的语法没有变,但编辑器的界面把语言折叠成了不同的样子。2016年夏天,邮件列表里出现了一封这样的帖子。

一个开发者询问,为什么他在自己的编辑器里看到的缩进风格和社区里大多数人的代码不一样。他用的是一款比较小众的编辑器,插件作者对Clojure的缩进惯例理解有偏差,导致自动缩进的结果与主流风格不符。这个开发者说他花了很长时间才意识到问题出在插件上,而不是他自己的理解上。在此之前,他一直以为Clojure社区对缩进没有统一意见,或者是他自己学错了。帖子发出后,几个有经验的开发者回复了他,解释了缩进惯例的形成过程,也推荐了几个配置方案。这个开发者后来更新了插件,问题解决了。

这件事很小,但它揭示了一个容易被忽视的机制。编辑器的默认行为,正在悄悄地塑造新人对语言的认知。一个自动缩进的结果,一个错误提示的措辞,一个补全列表的排序——这些看似中性的技术细节,实际上都携带着某种判断。它们告诉开发者:这样写是对的,那样写是错的;这个函数是常用的,那个函数是偏门的;这个错误信息值得关心,那个警告可以忽略。

这些判断并不是语言规范的一部分,但它们在日常使用中变得和语言规范一样有效力。一个开发者可能从未读过Clojure的官方风格指南,但他每天在编辑器里看到的缩进格式,会逐渐变成他心目中的正确写法。这种影响不是单向的。

编辑器环境也在吸收社区的审美共识,并把它固化到代码里。2013年,Clojure社区的非正式命名惯例逐渐稳定下来:库名要短小、具象、使用英语常用词;函数名用连字符分隔,而不是驼峰或下划线;命名空间用全小写字母加点号。这些惯例最初是通过邮件列表里的讨论和代码审查形成的,但很快就被编辑器插件采纳。插件开始自动补全时优先显示符合惯例的命名,错误提示也会建议开发者调整命名格式。于是,一种原先是软性的、靠口头传递的审美共识,变成了编辑器里的硬性规则。开发者不必知道这些惯例的历史,他们只需要在编辑器中按下回车键,习惯就会被自动执行。这个过程,和Python引入渐进类型有着相似的逻辑。

在这两个案例中,语言本身的设计并没有改变,但编辑器的界面层增加了一套新的视觉语法。Python的类型注解是一种显式的视觉语法,它告诉开发者这里应该补充类型信息。Clojure的编辑器惯例则是一种隐式的视觉语法,它不要求开发者写额外的东西,但通过颜色、缩进和补全的顺序,传递着关于好代码的判断。两种语法都存在于语言规范之外,但都深刻地影响了开发者如何阅读、如何书写、如何理解一门语言。

到这里,需要回头澄清一个容易被误解的地方。说编辑器界面在塑造审美共识,并不意味着社区成员变成了工具的被动接受者。实际情况要复杂得多。编辑器的插件不是从天而降的,它们是社区成员自己写的。Cursive的作者Colin Fleming从2012年开始独自开发这个插件,他本人就是一个Clojure开发者,他的设计选择反映了他对语言的个人理解。vim-fireplace的作者Tim Pope同样是一个使用Clojure多年的Vim用户。

这些插件的作者本身就是社区的一分子,他们参与邮件列表讨论,参加Clojure/conj,阅读别人的代码。他们把自己的审美判断写进了代码,然后这些代码又反过来影响了其他社区成员。这是一个循环的过程,而不是单向的灌输。

但循环并不意味着平等。一个插件作者的影响力,远远大于一个普通用户。插件作者的决定会影响成千上万的开发者,而普通用户的反馈只能影响插件作者的后续版本。这种不对称性,在社区规模扩大之后变得更加明显。早期用户可以自己写配置,甚至自己改插件源码,因为他们有足够的技术能力和时间。但后来的用户往往不具备这样的条件,或者不愿意投入这样的时间。他们使用插件的默认配置,接受默认的视觉风格,学习默认的快捷键。他们的审美判断,在很大程度上,是由插件作者预先做好的。

这就引出了本章的核心论断:编辑器的界面是媒介审美的最后一道沉淀,它使语言共同体的边界变得可见、可操作。

当开发者能够在自己的工具里顺畅地看见代码、触发错误、回看演示,那种原本抽象的语言共识便被写进了肌肉记忆。这里的肌肉记忆不是比喻。一个熟练的Clojure开发者,他的手指知道什么时候按下括号,什么时候按下回车,什么时候按下某个组合键触发补全。他的眼睛知道在哪里寻找错误信息,在什么颜色上停留,在哪个缩进深度上感到安心。这些身体经验,不是通过阅读语言规范获得的,而是通过重复使用编辑器获得的。它们构成了懂这门语言的一个不可分割的部分。

但肌肉记忆也有它的脆弱之处。一旦工具改变,肌肉记忆就会失效。一个Emacs用户切换到IntelliJ,即使他的Clojure知识完全没有变化,他也会感到自己不懂这门语言了,因为他的身体不再能够自动完成那些熟悉的动作。他需要重新学习快捷键,重新适应缩进风格,重新训练眼睛去寻找错误信息。这个过程可能持续数周甚至数月。

在这段时间里,他在技术层面依然是一个有经验的Clojure开发者,但在操作层面,他退化成了一个新手。这就是工具依赖的代价。手艺共识——通过REPL共享会话、代码审查和配对编程传递的隐性知识体系——一旦附着在特定工具上,工具的变迁就会威胁到共识的稳定性。

2017年,Clojure社区发生了一件与此相关的事情。当时,一个叫clj-refactor的Emacs工具包在社区中流行起来。这个工具包提供了一系列自动化重构功能,比如自动添加命名空间声明、自动整理import语句、自动把匿名函数转换成命名函数。这些功能极大地提高了开发效率,但也引发了争议。一些资深开发者认为,自动化重构会让新手跳过重要的学习过程。过去,一个开发者需要手动添加命名空间,在这个过程中他会理解命名空间的组织逻辑。过去,他需要手动整理import语句,他会注意到哪些库被频繁使用,哪些库已经过时。

现在,这些都被自动化了,新手只需要按几个快捷键,代码就整理好了,但他们对代码结构的理解可能停留在表面。这个争议没有得出明确的结论。clj-refactor继续流行,自动化重构成为越来越多人的日常操作。但争议本身指向了一个更深的问题:当编辑器足够智能,能够替开发者做判断时,开发者的判断力会发生什么变化?这个问题不限于Clojure,它是所有现代编程环境共同面临的困境。但Clojure社区的特殊之处在于,它的语言设计哲学强调简单和显式,而高级编辑器工具恰恰倾向于把复杂性隐藏在自动化背后。这两种力量之间的张力,在Clojure社区里比在其他语言社区里更加尖锐。

回到Python的渐进类型。Python社区的选择,实际上是在语法层面为编辑器工具提供了一个明确的锚点。类型注解不是强制的,但一旦你选择写注解,编辑器就能利用这些信息提供更好的服务。这是一种折中方案:保留了动态语言的灵活性,同时为工具链提供了结构化的信息。

Clojure社区没有走这条路。它没有在语法层面添加任何只为编辑器服务的特性。Clojure的解决方案是另一条路:通过更强的约定和更紧密的社区协作,来弥补缺乏静态类型信息带来的工具短板。

这个选择,既是技术上的取舍,也是审美上的坚持。但坚持是有代价的。到了2018年,越来越多的Clojure开发者开始使用IntelliJ加Cursive的组合,因为这个组合提供了最接近静态类型语言的补全体验。Cursive的作者通过大量的手工工作,为Clojure的核心库和常用第三方库编写了类型推断规则,使得IDE能够在不依赖类型注解的情况下提供准确的补全。这种努力令人敬佩,但它也揭示了一个残酷的事实:维持一个高质量的编辑器体验,需要有人持续投入大量时间和精力。

而这个人,并不是Clojure核心团队,而是一个独立的插件作者。如果Cursive停止维护,使用IntelliJ的Clojure开发者将面临体验的急剧下降。这种依赖关系,是脆弱的。

同样的情况也出现在其他工具上。Emacs的CIDER插件由Bozhidar Batsov维护,vim-fireplace由Tim Pope维护,Calva——VS Code的Clojure插件——由Peter Strömberg维护。这些工具构成了Clojure生态系统的外围基础设施,但它们的维护者大多是个人志愿者,而不是受雇于某家公司。Clojure社区在很长一段时间里,都依赖这些个人的善意和精力来维持编辑器的可用性。

这种模式在社区规模较小的时候运行良好,因为维护者的负担不重,用户的需求也比较一致。但当社区规模扩大,用户需求多样化,维护者的精力开始捉襟见肘时,问题就出现了。

2019年,Clojurists Together这个资助机制开始运作,它试图通过社区筹款来支持关键基础设施的维护者。CIDER是最早获得资助的项目之一。这是一项重要的制度创新,因为它承认了工具维护也是一种需要被支持的劳动,而不仅仅是个人爱好。

但资助机制的出现,也意味着工具维护不再是一种理所当然的存在。它需要被正式地组织、评估和分配资源。

这标志着Clojure社区的工具生态从一个自发的、基于善意的阶段,进入了一个需要制度化管理的阶段。这个转变本身,就是工具依赖深化之后必然出现的后果。

到了这个阶段,编辑器的界面已经不再是一个中性的助手。它开始分配一种感觉:谁更接近这门语言的内核。一个使用全功能IDE、享受流畅补全和即时错误提示的开发者,和一个使用基础编辑器、靠手动输入和REPL试验来写代码的开发者,他们对Clojure的体验是不同的,他们在社区中的位置也是不同的。

前者往往是在企业环境中使用Clojure的专业开发者,他们需要高效地完成工作,需要工具提供可靠的保障。后者往往是独立开发者、学生或者爱好者,他们更享受手动探索的过程,更重视语言本身的美学。这两种人都是Clojure社区的一部分,但他们使用不同的工具,看见不同的界面,发展出不同的习惯,也形成了不同的身份认同。

工具差异开始带有身份色彩。选择哪种编辑环境,有时会被读作属于哪一类开发者。

这种分类并不总是有敌意的,更多时候它是一种无声的识别。在邮件列表里,一个开发者贴出的代码片段带有特定的缩进风格,另一个人就能大致猜出他使用的是什么编辑器。在Clojure/conj的交流环节,开发者们互相询问的第一个问题,往往是对方使用什么编辑器,而不是做什么项目。这个问题本身,就是一次身份的确认。

回答会让对方在脑海中分配一个位置,一个标签,一组预设的期待。这种身份色彩,在2020年之后变得更加明显。

那一年,VS Code的Clojure插件Calva获得了显著的改进,吸引了大量新用户。这些新用户中,很多人之前没有使用过Emacs或IntelliJ,他们是从Web开发转入Clojure的。他们的到来,改变了工具生态的格局。VS Code的用户群体与Emacs和IntelliJ的用户群体在技术背景、工作环境和审美偏好上有明显的差异。

这些差异,通过编辑器界面的不同,被放大并且固化了。Calva的默认配色方案、快捷键设计和错误提示风格,与CIDER或Cursive不同。这些差异最初只是技术上的选择,但随着时间的推移,它们逐渐变成了不同子群体的身份标记。

现在,可以清楚地看到,语法高亮之后到底发生了什么。编辑器的界面从一种可选的装饰,变成了一种习惯性的判断,再变成了一种身份的标志。

这个演变过程,并不是Clojure社区独有的,但它在Clojure社区里展开的方式,带着这门语言特有的气质。Clojure的语言哲学强调简单、显式和不可变性,而编辑器的界面恰恰是一个不断变化、不断添加新功能的领域。这种张力,让Clojure社区的工具讨论总是带着一种特别的紧迫感。

每一次工具更新,每一次插件迭代,都在重新定义写Clojure意味着什么。2021年,Cursive的作者在博客上写了一篇文章,讨论IntelliJ插件的未来方向。

他提到,越来越多的用户希望插件能够提供更智能的代码生成功能,比如自动生成测试模板、自动补全复杂的函数组合。这些功能在静态类型语言中已经非常成熟,但在Clojure中实现起来要困难得多。他写道,他需要做出选择:是继续投入精力去逼近静态类型语言的体验,还是接受Clojure的动态特性,把重点放在REPL集成和即时反馈上。

这篇文章的评论区里,Clojure开发者们激烈地辩论。一些人认为,追求静态类型式的补全是在试图把Clojure变成它本不是的东西。另一些人则认为,如果编辑器体验跟不上,Clojure会在实际工作中输给那些工具链更成熟的语言。

这个辩论没有结论,但它揭示了一个基本的事实:编辑器的界面,已经成为Clojure社区关于语言未来的辩论的战场。什么是Clojure?

是里奇·希基设计的那个语法简单、强调不可变性的语言,还是开发者在编辑器里实际体验到的那个由颜色、快捷键和补全列表构成的界面?这两个Clojure,并不总是重合的。

而随着时间的推移,后者的影响力正在变得越来越大。因为大多数开发者接触Clojure的方式,不是通过阅读语言规范,而是通过打开编辑器,开始写代码。

肌肉记忆一旦形成,就会成为身体的一部分。改变肌肉记忆,比改变观念更难。一个开发者可以接受新的语法特性,可以学习新的库,可以调整自己的设计模式,但他很难适应一个不同的编辑器。

因为编辑器不是他思考的对象,而是他思考的环境。当环境改变时,他的整个工作流程都会被打乱。这就是为什么编辑器的选择会引发如此强烈的情绪反应。这不是一个关于偏好的讨论,而是一个关于身体经验、关于日常节奏、关于一个人如何成为某种类型的开发者的讨论。

Clojure社区在工具问题上的共识,从来不是通过权威强制达成的,而是通过示范、模仿和日常使用中的无声协商形成的。这个模式,和库的命名惯例、代码的排版风格、测试的书写习惯的形成过程,一脉相承。但工具问题有一个特殊之处:工具不是中性的载体,它本身具有能动性。

一个缩进规则,一个补全算法,一个错误提示的措辞,都在悄悄地塑造着使用者的判断。

这种塑造,不需要经过任何人的同意,不需要任何正式的文件,它发生在开发者每一天的日常操作中,发生在手指触碰键盘的瞬间,发生在眼睛扫过屏幕的那一刻。编辑器的界面,是Clojure社区共识机制中最日常、最隐蔽也最持久的一层。它不像邮件列表里的辩论那样可以被记录和引述,不像会议演讲那样可以被录像和回看,不像库的命名惯例那样可以被总结成文档。

它存在于每一次按键、每一次补全、每一次错误提示的闪烁中。它把抽象的语言共识,翻译成了身体可以感知、可以重复、可以依赖的操作序列。

当开发者能够在自己熟悉的编辑器里流畅地写Clojure代码时,他们感受到的不仅是一种技术上的便利,也是一种归属的确认。他们属于这个社区,因为他们和周围的其他人看见同一个界面,使用同一种手势,共享同一种审美。但归属的背后,也隐藏着排斥。

那些使用不同编辑器的人,那些无法获得相同工具体验的人,那些被自动补全和智能重构排除在知识传递路径之外的人,他们的归属感在哪里?当编辑器的界面开始分配谁更接近这门语言内核的感觉时,它也在无声地划定边界。

这个边界不是由任何人有意划定的,而是由工具的可及性、维护者的精力分配、社区的资源流向共同决定的。它是技术选择的结果,但它的影响远远超出了技术的范畴。

这就是媒介审美的最后一道沉淀完成之后,浮现出来的真相。工具不再是中性的助手。它已经在分配感觉,定义归属,塑造身份。而这一切,都发生在语法高亮之后,都沉淀在每一个开发者每天打开编辑器时,第一眼看见的那片彩色的括号之间。