Martin Odersky 访谈(第一部分)
by Bill Venners and Frank Sommers
译者: 杨博
摘要 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 向上兼容, 有些事情, 我们想要做得不同.
来源: https://juejin.im/entry/5c00d2556fb9a049c042c079