拾贝

【翻译】Scala的起源 —— Martin Odersky访谈(一)

    coding     翻译·Scala

Scala是一种运行在JVM之上的通用的,面向对象的函数式语言。它的作者是Martin Odersky,一位来自洛桑联邦理工大学(EPFL)的教授。作为本系列访谈的第一部分,Martin Odersky和Artima网站的Bill Venners一起讨论了Scala的历史和起源。

结缘编译器

Bill Venners: 让我们回到最初。你是如何接触编程语言的?

Martin Odersky: 编译器和编程语言从来都是我最爱的课题。1980年,那时我还在读本科,当我第一次搞明白什么是编译器,我立刻就想自己构建一个。我当时唯一能买的起的电脑就是拥有1K内存的Sinclair ZX 80。我差点就能一试身手,不过幸运的是,很快我接触到一台更强大的电脑,Osborne-1。它是世界上第一台便携式电脑,从远处看起来就像一台倾斜了90度的缝纫机。它拥有5英寸屏幕,每行显示52个字符。最令人印象深刻的是它拥有56K内存以及两个90K的软盘驱动器。

那段时间,我经常和学校里一名叫Peter Sollich的学生在一起。我们通过阅读了解到一门新的被称为Modula-2的语言,我们发现这门语言不仅非常优雅而且设计也很棒。于是我们就产生了为8位的Z80电脑设计Modula-2编译器的念头。不过Osborne有一个小问题,它只自带微软Basic这一门语言。这跟我们的预想完全不相符,因为Basic语言只支持全局变量,甚至不支持带参数的过程。而当时其它的编译器对我们而言都太贵了。于是我们决定采用经典的引导技术(bootstrapping technique)。Peter曾经用Z80的汇编语言写过一个支持部分Pascal语法的编译器。我们使用这个编译器编译了更大一点的语言,然后再更大一点,迭代数次后,直至我们能够编译所有Modula-2的语法。这个新的编译器能够生成用于解释执行的字节码以及相应的Z80上的二进制文件。这些字节码是当时任何系统上所能生成的最紧凑的字节码,相应的二进制版本也是8位电脑上运行最快的。这个编译器在当时可以说是相当棒的。

就在我们快完成我们的编译器时,Borland推出了Turbo Pascal,并且准备进军Modula-2市场。实际上,Borland决定购买我们的Modula-2编译器,重新冠以Turbo Module-2(CP/M版本)的名称和另一款他们打算开发的IBM PC版本一起搭售。我们提出为他们开发这个IBM PC版本,但是他们告诉我们他们已经有人在着手做了。三或四年之后,当这款编译器最终面世时,实现它的团队已经从Borland分离出来,并且为这款编译器起了一个新的名字,TopSpeed Module-2。在缺少IBM PC版本的情况下,Borland从来没有进行过任何Turbo Modula-2的市场推广,因此它也一直默默无名。

当我们完成Modula-2编译器时,Borland立刻提出聘请Peter和我。Peter加入了他们。我差点也去了,但是我还有一年的课程以及研究生计划。我当时对从大学辍学的念头也很动心。最后,我还是决定留在学校。在我读研期间(研究课题是增量解析),我发现我非常喜欢做研究。所以最终,我放弃了去Borland写编译器的念头,转而去ETH Zurich跟随Pascal和Module-2的发明者Niklaus Wirth攻读博士学位。

更好的Java

Bill Venners: Scala是如何产生的?它的历史是什么样的?

Martin Odersky: 1988、1989年之间,当我即将结束在Zurich的学业,我对函数式编程产生了极大的兴趣。所以我留下来继续做研究,最后成为了德国Karlsruhe大学的一名教授。一开始我专注于偏理论的编程领域,比如按需调用(call-by-need)的λ演算。这部分工作是和当时在Glasgow大学的Phil Wadler一起进行的。一天,Phil告诉我他组里的一个助教听说有一门新的语言正在兴起,目前处于Alpha版本,名字叫Java。它是可迁移的,能够产生字节码,运行在web端,并且有垃圾回收机制。这门语言将颠覆你的工作。你准备怎么办?Phil说。好吧,他也许是对的。

回答是Phil Wadler和我决定将一部分函数式编程的概念植入到Java世界中。这部分工作诞生了一门叫Pizza的语言,它拥有3个函数式编程的特性,泛型,高阶函数和模式匹配。Pizza首次发布于1996年,在Java面世后的一年。它成功的证明了在JVM平台上是能够实现函数式语言特性的。

于是我们联系了Sun公司核心开发团队的Gilad Bracha和David Stoutamire。他们回应说,“我们对你们正在做的泛型那部分工作很感兴趣。我们可以开始一个新的专注于此的项目。”那个项目被称为GJ(Generic Java)。1997、1998年之间,我们完成了GJ的开发。六年后,加上一些之前我们没做的特性,它成为了Java 5中的泛型。值得一提的是,Java泛型中的通配符是在我们之后由Gilad Bracha和Aarhus大学的其他人一起独立完成的。

虽然我们的泛型扩展延期了六年才推出,Sun对我写的GJ编译器产生了更强烈的兴趣。它被证明比Sun最初版本的Java编译器更稳定也更易维护。所以他们决定从2000年发布的Java 1.3版本起,使用GJ编译器作为标准的Java编译器。

超越Java

Martin Odersky: 在开发Pizza和GJ期间,我有时感到绝望,因为Java是一门限制性很强的语言,很多事情不能按我自认为正确的方式去做。因此,在那之后,当我实质性的工作目标转移到让Java更好上时,我决定是时候后退一步了。我想从头来过,看看我能否设计出一门比Java更好的语言。但同时我知道我不能从零开始,我必须借助于已有的基础架构,没有任何类库、工具的支持去启动这样一个项目是不现实的。所以我决定虽然我的目的是设计一门与Java不同的语言,但这门语言仍然可以与Java的基础架构(JVM和类库)互连。这就是我(设计Scala语言)的初衷。也是在那个时候,我成为了EPFL的一名教授,这为我提供了一个绝佳的进行独立研究的环境。我得以成立一个研究小组,无需成天申请外部津贴。

起初我们非常激进。我们想创建一门基于一个被称为join calculus的非常完美的并发模型上的语言。我们创建了一个被称为Functional Nets的面向对象版本的join calculus实现,以及一门基于其上的语言Funnal。但没过多久,我们发现作为一门很纯粹的语言,Funnel并没有太多实际意义。Funnal有一个很小的内核。很多大家认为理所应当包含的东西(比如类,模式匹配)只能通过编码的方式植入到内核中。从学术的角度,这是一种很优雅的技巧。但从实践角度,并不是太好。初学者发现这类编码非常困难,而专家们又认为过于重复和单调。

结果是我们决定重新开始,在非常纯粹的学术性语言Funnal和非常实用但又在一定程度上带有限制性的GJ之间寻找出路。我们想创造一门不仅实用同时又比Java更高级的语言。大约在2002年,我们开始开发这门后来被我们称为Scala的语言,并于2003年发布第一版本。2006年初,又发布了一个相对比较大的重构版本。在此之后,就进入一个稳定的迭代期。

挑战

Bill Venners: 你曾说有时候为了保证与Java的兼容性让你非常抓狂。你能给一些具体的起初由于兼容性限制你做不了,但后来从源代码兼容性降为二进制兼容性之后又可以做的例子吗?

Martin Odersky: 在泛型设计中,存在很多非常非常困难的限制。最强的也是最难解决的限制是需要与没有泛型支持的Java版本兼容。这个问题的背景是Java 1.2版本发布了没有泛型支持的集合类库,那时泛型刚出现,Sun公司还没准备好发布一个全新的基于泛型的集合类库。

那就是为什么存在这么多非常丑陋的设计的原因。你总是要同时面对非泛型类型和泛型类型(也被成为原始类型)。同样你也不能改变数组的行为,于是你就不得不接受未受检警告。最重要的是,在操作数组时很多你想做的事情是做不了的,比如生成一个拥有未知类型T的数组。后来在Scala中我们实际上发现了一种实现的方式,但那是由于我们给数组加入了协变的特性。

Bill Venners: 你能够详细说明一下有关Java中协变数组的问题吗?

Martin Odersky: 最初Java刚发布时,Bill Joy和James Gosling以及其他Java组成员都认为Java应该支持泛型,只是他们没有时间去完善设计并加入进去。正因为最初版本的Java不支持泛型,他们感觉数组应该是协变的。举例来说,那就意味着一个String数组是一个Object数组的子类型。背后的原因是他们想实现一个类泛型的排序方法,接受一个Object数组和一个排序类然后对这个数组进行排序,同时允许你传入一个String数组。结果发现这个设计通常是有缺陷的。那就是为什么在Java中你会得到一个数组存储异常。实际上这个设计最后也阻止了实现一个优雅的泛型数组。那就是为什么Java泛型中根本不支持数组。你不能创建一个类型为String列表的数组,这根本不可能。你永远不得不使用丑陋的原始类型,一个列表类型的数组。所以某种程度上这就是一个原罪。他们完成的很快,认为这只不过是一次快速改造。但实际上这毁掉了之后每一个设计决定。因此,为了避免掉入同样的陷阱,我们不得不停下来宣布,现在我们不再和Java保持向前兼容,我们想做一些不同的事。

查看英文原文:The Origins of Scala

该系列访谈的其余部分: