Scala起源

398 阅读10分钟
原文链接: zhuanlan.zhihu.com

Scala起源

Martin Odersky访谈(第一部分)

by Bill Venners and Frank Sommers

译者:杨博

原文:The Origins of Scala


摘要 Martin Odersky与Bill Venners谈论Scala编程语言如何创立的相关历史。

Scala,一门通用用途、面向对象、函数式的JVM语言,是瑞士洛桑联邦理工大学教授Martin Odersky的心血结晶。本访谈系列由多部分组成。本文是第一部分,Martin Odersky与Artima网站的Bill Venners谈论了Scala的历史。

发现编译器的魅力

Bill Venners: 我们从头开始谈谈吧。你最早是如何开始涉猎编程语言的呢?

Martin Odersky: 编译器和编程语言一直都是我最喜欢的主题。我第一次钻研编译器是在1980年。那时我还是个本科生,想编写一个自己的编译器。 当时我唯一能远程用上的电脑是Sinclair ZX 80。它上面有1KB内存。我差点就在上面开始尝试了。但幸好我很快就得到更强大的机器Osborne-1。这是世界上第一款“便携式”计算机,远看就像一台倾斜90度的缝纫机。 它的屏幕五英寸大,每行只能显示52个小小的字符。但它亦有惊人之处。内存多达56KB,软盘驱动器有两个,每个容量90KB。

那些日子,我花了一些时间与另一个叫做Peter Sollich的同学呆在一起。我们学了一门新语言Modula-2。我们觉得它既优雅又精致。 于是我们诞生出了一个新计划:我们要为8位计算机Z80编写Modula-2编译器。编写时有个小问题,Osborene附带的唯一语言是微软的Basic语言。Basic语言完全不合乎我们设想,它甚至不支持带参数的例程——除了全局变量就别无所有了。而当时其他编译器对我们穷学生来说太贵了。 因此,我们决定采用经典的自举(bootstrapping)技术。 Peter用Z80汇编语言写了第一个编译器,支持Pascal的小部分语言子集。接着,我们使用这个编译器来编译一个稍大的语言。以此类推,在几次迭代后,我们终于可以编译完整的Modula-2代码了。它可以生成解释型字节码,以及Z80二进制文件。 它生成的字节码,在当年所有系统中,文件最小;它生成的二进制文件,在8位计算机中,速度最快。在当时,算得上能力很强的系统了。

在我们快完成编译器之时,Borland公司推出了Turbo Pascal,还在考虑进入Modula-2市场。事实上,Borland公司决定购买我们的Modula-2的编译器、命名为Turbo Modula-2再卖到CP/M,此外他们还想开发IBM PC版。 我们愿意为他们开发IBM PC版,但他们告诉我们,他们另有计划。 不幸的是,他们开发IBM PC版所费时间远超预期。 产品发布已经是三四年后。实现团队已经从公司分拆,成为了我们所知的TopSpeed Modula-2。由于缺了IBM PC版,Borland公司从未为Turbo-Modula-2提供营销资源, 所以它一直没什么名气。

我们完成Modula-2编译器之际,Borland公司立时就邀请我和Peter加入。Peter去了他们公司。我差点也去了。但我的问题是,我还有一年课程要读,还有硕士项目要做。那时我差点没忍住辍学的诱惑。 最后,我决定坚持上大学。 在我硕士项目期间(关于增量解析),我发现我挺喜欢科研的。所以最后,我放弃加入Borland编写编译器,转而攻读博士学位。我的导师是Pascal和Modula-2的发明者,苏黎世联邦理工学院的Niklaus Wirth。

改善Java的工作

Bill Venners: Scala是怎么诞生的?有什么历史吗?

Martin Odersky: 在我快要离开苏黎世时,大约1988年至1989年,我越来越喜欢函数式编程了。所以我继续科研之路,并最终成为德国卡尔斯鲁厄大学教授。 我最初工作更偏向编程的理论方面,比如call-by-need lambda演算. 这项工作与Phil Wadler共同完成。当时他在格拉斯哥大学。 有一天,Phil告诉我,他的组里有个热血助理听说一门新的语言刚出来,仍处于Alpha阶段,名叫Java。 助理向Phil宣告:“瞧瞧我大Java,它有移植性,有字节码,能运行在Web上,还有垃圾收集。这东西可以完爆你。你打算怎么办呢?”Phil说:“呃,可能他说得有点道理。”

答案是,我和Phil Wadler决定从函数式编程中提取一些点子,移植到Java界。 我们的努力转化成为一门名叫Pizza的语言,其中具备函数式编程的三大特性:泛型、高阶函数和模式匹配。Pizza首次发布于1996年,即Java推出后一年。Pizza还算成功,因为它表明,JVM平台上可以实现函数式语言特性。

接着,Sun核心开发团队的Gilad Bracha和David Stoutamire联系了我们。他们说:“我们对你已经做的泛型什么的东西还真挺感兴趣。我们来做个新项目专注这个功能吧。”这个新项目就成了GJ(Generic Java)。 因此,我们在1997/1998年开发了GJ。6年后,GJ再加上当时我们还没做的额外功能,就成为了Java 5中的泛型。那个没做的额外功能就是Java泛型的通配符功能,后来由Gilad Bracha和奥胡斯大学的人独立开发。

虽然我们的泛型扩展被搁置了6年,但Sun对我为GJ写的编译器产生了强烈的兴趣。有证据表明,GJ比他们的第一个Java编译器更稳定,更易于维护。 因此,他们决定,在2000年发布的Java 1.3版及以后版本的中,都采用GJ编译器作为标准javac编译器。

设计比Java更好的语言

Martin Odersky: 此刻,在Pizza和GJ的经验中,我常常感到沮丧,因为Java是一门具有硬性约束的语言。因此,我做很多事情时都不能用我本来想用的方式,不能用我确信正确的方式来做。毕竟那时候我的工作重点是改善Java。所以,在那段时期过去以后,我决定,我该退一步了。我想从一张白纸开始,看看我能不能设计出比Java更好的东西。但同时我又知道我不能完全从头开始。 我必须利用上现有的基础设施,不然的话,光是无中生有的自举,没有任何库,工具之类的东西,根本就不现实。

所以我决定,即使我想设计不同于Java的语言,总归得连接到Java的基础设施——即JVM和Java库。 我的想法就是这样。 当时我正有个大好机会,因为当时我在洛桑联邦理工学院做教授,这让我有了独立研究的良好环境。我可以组成一个小团体的研究人员,可以专心工作,不会因外部的赞助人而耗光我所有的时间。

起初我们相当激进。 我们想创造的东西,基于一种非常优美的并发模型,叫做join演算。我们创造了面向对象版的join演算,叫做Functional Nets,还创造了一门语言,叫做Funnel。过了一段时间,我们发现,Funnel语言的确非常纯粹,但却算不上很实用。 Funnel根植于非常小的内核。 人们认为理所当然应具备的功能(比如类、模式匹配)都必须要对内核编码才能提供。从学术角度看,这技术非常优雅。但实践中不大行得通。 初学者想要找出该怎么编码会相当困难,而专家却发现一次又一次重复编码又很无聊。

结果,我们决定再次重头做点事情,介于纯粹的学术语言Funnel,以及非常务实但受限重重的GJ之间。我们要创造的东西,将会既具有实用价值,又比Java中的先进。2002年时,我们开始开发这门新的语言。我们叫它Scala。首次公开发布于2003年。 2006年年初时有一次相对较大的重新设计。 此后它一直持续成长并逐渐稳定。

改善Java时所受的约束

Bill Venners: 你说,你需要与Java向后兼容,你受到重重约束,因而多次让你感到沮丧。你能否具体举些例子说说,有哪些事,戴着镣铐跳舞时无法做到,而只保证二进制兼容但无视源代码兼容时就可以做到?

Martin Odersky: 设计泛型时,我遇到许多非常非常紧的约束。最紧的约束,最难应付的,就是被迫要完全向后兼容无泛型Java。这个故事是这样的,Java1.2才刚刚加入集合库,Sun不想仅仅因为推出了泛型就另外新增一整套集合库。而不这么做,泛型就得以完全透明的方式实现。

这就是存在一些相当丑陋的东西的原因。 你不得不处处让无泛型类型和泛型类型一起工作,即所谓的raw类型。而且你还不能改变数组的行为不然就会碰上unchecked警告。最重要的是,处理数组时无法按你想要的方式来做,比如创建具有类型参数T的数组,或者创建你还不知道元素类型的数组。反正你就是没办法做。后来我们在Scala中确实找到了办法,但前提条件是我们让Scala的数组不再支持协变和逆变。

Bill Venners: 你能否阐述一下Java的协变数组有些什么问题吗?

Martin Odersky: Java发布初版时,Bill Joy、James Gosling以及Java团队的其他成员都觉得Java就该支持泛型,可惜他们没有时间好好设计。所以考虑到Java(至少初版)不支持泛型,他们觉得至少要让数组能支持协变。这就意味着,举例说吧,String数组就是Object数组的子类型。这么做的原因是,他们想要支持编写某种泛型排序方法,接受一个Object数组参数,以及一个比较器(comparator)参数,而该方法可以把Object数组排序。然后,允许你把String数组传入其中。总而言之,这类设计算得上一般意义上的类型错乱了。这就是你在Java中会遇到数组修改异常的罪魁祸首。实际上,也正是它导致泛型数组无法正常实现以及数组在Java泛型中根本没法用的原因。你不能声明字符串列表的数组,压根就做不到。你必须用丑陋的raw类型,只能永无止尽地使用列表的数组。因此,它有点像原罪。 他们做得很快,觉得这只是权宜之计。它实际上把未来的每一次设计决定全都毁了。 因此,为了不再次陷入同样的陷阱,我们不得不另寻他路。现在我们宣布,不会与Java向上兼容,有些事情,我们想要做得不同。