见证一门新编程语言的诞生是一件有趣的事。对任何一位使用编程语言的人而言——无论你是首次尝试编程的人还是职业软件工程师——编程语言看起来就在那里。就像锤子或斧子一样,编程语言也是一种工具,让我们可以做我们想做的。我们很少会想到工具是怎么来的,它的设计过程是怎样的。或许我们对工具的设计有自己的看法,但除此之外,我们通常只能接受并运用它。

编程语言的创造过程让我有了完全不同的视角。各种可能性似乎无穷无尽。同时,编程语言也必须满足看起来同样无穷无尽的各种约束。这是一种奇怪的张力。

创造一门新的编程语言有很多方面的原因,例如,某种个人想要解决的“痛点”,或者某种学术上的洞见,或者技术债,或者其他编译器架构的潜在收益,甚至可能是政治因素。对Scala 3的创造而言,上述原因多少都有一些。

无论出于何种原因,一切都开始于Martin Odersky在某一天突然消失,当他几天后再次出现在某个研究组会议上时,他向大家正式宣告自己已经开始尝试从零起步编写一个全新的编译器,将DOT演算[1]实施落地。而在场的我们是一群博士研究生和博士后,在那之前主要负责Scala 2的开发和维护。当时,Scala看起来正在接近看上去难以企及的成功高度,尤其是对于这样一门诞生于瑞士的、一个听起来有些奇怪的学校的偏门学术编程语言。然而就在不久前,Scala得到了旧金山湾区的很多创业公司的追捧,成立了Typesafe也就是后来的Lightbend,专注于支持、维护和管理Scala 2。那么为什么突然要做一个全新的编译器,以及由此带来的不一样的编程语言呢?我们当中的大多数人对此心存疑虑,但Martin Odersky已经下定了决心。

几个月过去了。就像上了发条一样,每天中午12点,整个实验室的人都会出现在连接各个办公室的门厅。当聚集了一定数量的人员以后,我们就会一起来到EPFL的某个餐厅吃午饭,并享用饭后咖啡。在每天都会举行的这个“仪式”中,关于新编译器的想法是反复出现的讨论话题,例如,从“150%”兼容Scala 2(避免陷入Python 2和Python 3的困境),到创造一门全新的“全光谱”依赖类型编程语言。

研究组中持怀疑态度的人,一个接一个地被Scala 3的某个特性征服,比如,对类型检查器的精简,全新的编译器架构,以及对类型系统的增强等。随着时间的推移,社区主流也认为Scala 3相比Scala 2而言具有显著的改进。对于这个结论,不同的人有不同的理由。对有些人而言,是因为Scala 3将花括号和条件判定语句的括号变为可选的,从而改善了可读性。对其他人而言,是因为Scala 3的类型系统更强了。如此种种。

我可以很有信心地说,Scala 3的设计并不是完全依靠直觉的“闭门造车”,而是吸纳了过去设计的宝贵经验,以及EPFL研究组和Scala社区的多年沟通与交流经验。并且,除从头开始在全新的“地基”上搭建之外,别无他途。既然Scala 3是从头开始设计的,其内核就是一门全新的编程语言。

Scala 3是一门全新的编程语言。诚然,它兼容Scala 2,听起来像是一门已经存在的编程语言的第三个重大版本。但是不要被这个影响了你的判断,Scala 3实现了在Scala 2中先行试验、探索的诸多想法的重大精简。

在Scala 3的所有特性中,可能最“Scala”的一个专属的变化是对隐式的改动。Scala从一开始就被聪明的程序员们用来实现某种基于Scala特性本就很少有人能想到的功能,更别提这些功能与Scala设计本意的背离程度有多大了。这个先前被称作隐式的特性可能是Scala中最著名的被用来以各种奇怪的方式改变Scala 2代码行为的功能点了。隐式的使用场景包括:对一个类在“事后”追加方法,不扩展也不重新编译;或者,在某种特定的上下文中,基于某种类型签名,自动选择适用于该上下文的正确实现。上述只是冰山一角——我们甚至为此写了一篇论文以对开发人员使用隐式的各种方法进行归类。[2]

这就像是把旋钮和杠杆交给用户,期待他们能做出一台精密的仪器,如图1所示的机械计算器。但通常我们得到的是类似于Theo Jansen的动力雕塑,如图2所示,而不是某种能一眼看出用途的物件。[3]简单而言,如果你交给编程社区的是一些旋钮和杠杆,则社区中那些强悍的选手总能找到这些工具的创新用法。这是人的本性。不过可能正是在这里,Scala 2犯了错误,将最基础、最通用的旋钮和杠杆交给了程序员。

图1 机械计算器

图2 类似于Theo Jasen的动力雕塑

我想说的是,在Scala 2中,隐式有无穷无尽的可能性,这些可能性足够我们撰写研究生论文,而社区对于如何使用隐式并没有一个统一的认识。这种没有清晰用途的编程语言特性不应该存在。但是很可惜,隐式就是这样一种存在:很多人将隐式看作Scala独有的强大功能,没有其他语言能做到;还有很多人认为隐式是神秘且经常令人困惑的机制,会侵入你的代码,将你的代码改得面目全非。

你可能已经听说过很多种不同形式的表述,但Scala 3代表了这之前所有Scala版本的简化版本。隐式是一个很好的例子。在意识到那些“后空翻”程序员希望通过隐式来实现更广泛的编程模式[如类型族(typeclass)派生]后,Martin Odersky在其他人的帮助下得出的结论是,我们不应该把注意力集中在人们一般如何使用隐式作为编程机制,而应该关注程序员们想用隐式做什么,然后把这个目标变得更加容易且高效。这就是口头禅“Scala 3专注于意图而不是机制”的来源。

Scala 3并不把注意力集中在作为编程机制的隐式的通用性上,而是关注开发人员在使用隐式时想要满足的特定使用场景,让其用起来更加直接。例如,隐式地将上下文或配置信息传递给某方法,而不需要程序员显式地给出重复的参数;在“事后”给类追加方法;在算术运算中对不同类型的值进行转换。如今,Scala 3将这些使用方法直接提供给程序员,使他们不需要“深入”理解Scala编译器如何解析隐式值,只需要关心“在不重新编译Bar类的前提下给Bar类追加foo方法”这样的任务即可;不需要具有博士学位,只需要把之前的“隐式”替换成其他更直接的与特定使用场景相关的关键字即可,如given和using。更多内容参见第21章和第22章。

“专注于意图而不是机制”的故事并不止于对隐式的改造,这个设计哲学几乎贯穿了这门语言的方方面面。例如,对Scala类型系统的增强和简化,包括并集类型(union type)、枚举(enum)、匹配类型(match type)等;对Scala语法的清理,包括if、else、while,让条件判断读起来更像英文。

当然,我说的这些,你不必盲目相信。无论你是Scala新手还是有经验的Scala开发人员,我希望你和我一样,Scala 3所包含的许多新的设计理念能让你感到耳目一新且直截了当!

Heather Miller
瑞士洛桑
2021年6月1日

[1]DOT(Dependent Object Type,依赖对象类型)演算是一系列形式化的推演,尝试对Scala类型系统的本质进行提炼和描述。

[2]Krikava等,《Scala隐式无处不在》[Kri19]

[3]关于Theo Jansen动力雕塑(标题为Strandbeest)的更多动态效果展示参见网址列表条目[1](译者注:网址列表获取方式见读者服务)。