1.1 一门按需伸缩的语言

不同大小的程序通常需要不同的编程概念。比如下面这段小程序:

这个程序首先设置好国家和首都之间的一组映射,然后修改映射,添加一个新的绑定("Japan" -> "Tokyo"),最后将法国(France)的首都(Paris)打印出来。[2]本例中用到的表示法高级、到位且没有多余的分号或类型标注。的确,这段代码看上去感觉像是一种现代的“脚本”语言,如Perl、Python或Ruby。这些语言的一个共通点,至少就上面的示例而言,是它们各自都在语法层面支持某种“关联映射”(associative map)的结构。

关联映射非常有用,因为它让程序精简可靠,不过有时你可能不同意这种“一体适用”(one size fits all)的哲学,因为你需要在你的程序中更为精细地控制映射结构的属性。Scala给你这种自由度,因为映射在Scala里并不是语言本身的语法,它是通过类库实现的一种抽象,可以按需进行扩展和适配。

在上面这段程序中,得到的是默认的Map实现,不过改起来也很容易。比如,可以指定一个特定的实现,如HashMapTreeMap,也可以通过调用par方法得到一个并行执行操作的ParMap。可以指定映射中的默认值,也可以在创建的映射中重写任何方法。无论是哪一种定制,都可以复用与示例中相同的易用语法来访问你的映射。

这个示例显示,Scala既能让你方便地编写代码,也提供了灵活度。Scala有一组方便的语法结构,可以帮助你快速上手,以愉悦而精简的方式编程,同时,你也会很放心,因为你想实现的并不会超出语言能表达的范围。你可以随时根据需要裁剪自己的程序,因为一切都基于类库模块,任由你选用和定制。

培育新类型

Eric Raymond首先提出了大教堂和市集的隐喻,用来描述软件开发。[3]大教堂指的是那种近乎完美的建筑,修建需要很长的时间,不过一旦建好,就很长时间不做变更。而市集则不同,每天都会有工作于其中的人们不断地对市集进行调整和扩展。在Raymond的著作里,市集用来比喻开源软件开发。Guy Steele在一次以“培育编程语言”为主题的演讲中提到,大教堂和市集的比喻也同样适用于编程语言的设计。[4]在这个意义上,Scala更像是市集而不是大教堂,其主要的设计目标就是让用Scala编程的人们可以对它进行扩展和定制。

举个例子,很多应用程序都需要一种不会溢出(overflow)或者说“从头开始”(wrap-around)的整数。Scala正好就定义了这样一个类型scala.math.BigInt。这里有一个使用该类型的方法,用于计算传入整数值的阶乘(factorial):[5]

现在,如果你调用factorial(30),将得到

BigInt看上去像是内建的,因为你可以使用整型字面量,并且对这个类型的值使用*-等操作符运算。但实际上它不过碰巧是Scala标准类库里定义的一个类而已。[6]就算没有提供这个类,Scala程序员也可以直接(比如,对java.math.BigInteger做一下包装)实现。实际上,Scala的BigInt就是这么做的。

当然,也可以直接使用Java的这个类。不过用起来并不会那么舒服,因为虽然Java也允许用户创建新的类型,但是这些类型用起来并不会给人原生支持的体验:

BigInt的实现方式很有代表性,实际上还有许多其他数值类的类型(大小数、复数、有理数、置信区间、多项式等)。某些编程语言原生地支持其中的某些数值类型。举例来说,Lisp、Haskell和Python实现了大整数;而Fortran和Python则实现了复数。不过,如果某个语言要同时实现所有这些对数值的抽象,则只会让语言的实现变得大到不可控的程度。不仅如此,就算有这样的语言存在,总有某些应用会得益于语言提供的范围之外的类型。因此,试图在语言中提供一切的做法并不实际。Scala允许用户通过定义易于使用的类库来培育和定制,最终的代码让人感觉就像是语言本身支持的那样。

培育新的控制结构

从前一个示例中可以看到,Scala允许我们添加新的类型,且这些类型的用法与内建的类型相同。像这样的扩展原则也适用于控制结构。在ScalaTest这个颇为流行的Scala测试类库中,AnyFunSuite编程风格就很好地展示了这一点。

举个例子,下面是一个简单的测试类,包含了两个测试:

我们并不指望你在现阶段就能完全理解AnyFunSuite这个例子,重点是在伸缩性方面,无论test还是assertThrows语法都不是Scala内建的。虽然它们看起来及运行起来都与内建的控制结构很像,但是事实上它们都是在ScalaTest类库中定义的方法。这两个控制结构完全独立于Scala编程语言本身。

从这个例子中不难看出,哪怕是具体到软件测试这样的特定场景下,你也可以朝新的方向“培育”Scala语言。当然,为了做到这一点,我们需要有经验的架构师和程序员,不过关键在于这是可行的——可以用Scala设计和实现那些能解决全新应用领域问题的抽象,且它们用起来就像是语言原生支持的一样。