title: "Scala 创始人 Martin Odersky:语言比较与 AI 如何重塑编程"
source_url: "https://www.youtube.com/watch?v=LdN4sPWM-WY"
author: "Ryan Peterman"
excerpt: "公共早报 Scala 创始人 Martin Odersky 比较多种编程语言的取舍,并指出 AI 生成软件将让强类型、内存安全、能力系统和可长期维护的规格成为人类工程师保持控制力的关键。"
简介
Martin Odersky,Scala 的创始人,讨论了函数式编程、Scala、Rust、Go、Zig、Python 和其他语言之间的权衡,以及 AI 生成代码如何改变编程语言的角色。他认为,随着工程师越来越多地引导和验证 AI 而不是自己编写每一行代码,强类型、内存安全、明确的能力和精确的规格将变得更加重要。
目录
函数式编程与实用纯粹性
Scala 的函数式和面向对象编程结合
系统语言:Rust、Go、Zig 和垃圾回收
动态语言、Python 和 Scala 的影响
JVM、编译器以及 Scala 在 Twitter 的采用
AI 生成代码、类型和能力
规格、提示词和未来工程职业
学习建议、学术界和 Scala 的经验教训
函数式编程与实用纯粹性
主持人: 什么是函数式编程,为什么命令式程序员应该关心它?
Martin Odersky: 函数式编程就是用值编程。程序使用值和将一个值转换为另一个值的函数,而不是使用内容会改变的可变变量或数组。这是故意限制性的。真正的函数式语言不是完全纯粹的:程序最终必须与状态交互、写入输出或执行其他效果。目的是推迟并隔离这些效果,使程序的大部分仍然是值和转换的组合。
这种分离使程序更可预测并减少错误。对全局变量、对象字段或某些共享状态的影响通常是函数文档未记录的后果。它可能只在注释中描述(如果有的话),在推理程序时很难记住。函数式编程建议将这些效果减少到最低限度,或者在函数可以简单地将输入映射到输出时消除它们。
它也接近数学。多项式、字符串和列表的数学理论没有突变:转换产生一个新的多项式而不是改变一个系数然后假装它仍然是同一个对象。函数式编程遵循这种计算观点。
主持人: 你会对因为不方便而回避函数式编程的人说什么?
Martin Odersky: 有一条学习曲线。大多数程序员首先学习命令式语言,所以最初可能很难看到如何以不同方式表达程序。但只要坚持一些,好处很快就会到来:程序变得更清晰、更容易理解,更不依赖于在调试器中逐步跟踪执行。他经常不需要为函数式代码使用调试器。
也有纯粹性变得不方便的地步。一些方法试图将几乎所有东西表达为纯函数,并通过 monad 来推迟效果。这可能很快变得笨拙。合理的答案取决于具体情况:如果副作用有良好的文档且使用适度,是可以接受的。函数式编程可能对程序的大部分有价值,而剩余部分的命令式功能完全合理。
有时研究表明 Scala 等语言的 bug 比 C 或 C++ 少,但 Odersky 不想夸大这个结论。实证软件工程研究很难设计和解释。他自己的判断是,随着系统增长,函数式静态类型语言变得更有价值。小程序可以按偏好使用循环或递归;在更大的系统中,静态类型已被证明是有用的。当正确性必须特别强时,如在 Rocq 或 Lean 等证明系统中,函数式语言是自然的设置。
Scala 的函数式和面向对象编程结合
主持人: 是什么让 Scala 与众不同,为什么有人现在应该学习它?
Martin Odersky: Scala 同时是一种有能力的函数式语言和一种有能力的面向对象语言。它最初的想法不是将两种风格并列,而是将它们的特点结合成一个连贯的综合。这在很大程度上是成功的:这种结合可以产生漂亮的程序。
面向对象编程对组件、模块、封装和接口特别有用。函数式编程并不总是为这些关注提供同样强的描述,尽管 Standard ML 和 OCaml 等语言有有能力的模块系统。当人们谈论函数式编程时,他们通常较少关注组件和接口,尽管这些在真实系统中非常重要。
在 Scala 中,对象贯穿整个语言。Odersky 引用了 Simon Peyton Jones 所说的"点的力量":有了对象和点,编程环境立即呈现相关的字段和方法。这集中了程序员的注意力。在纯函数式设置中,你可能面对一大片函数,它们可能被应用于一个参数,而你必须确定哪一个是合适的。
系统语言:Rust、Go、Zig 和垃圾回收
主持人: 在 Rust、Zig、Go、C 和 C++ 中,哪些系统语言脱颖而出?
Martin Odersky: 今天的系统语言需要保证的内存安全。这排除了一些候选者,让 Rust 和 Go 成为特别认真的选择,尽管它们在不同的层次上运作。Go 不是 primarily 一个螺母和螺栓嵌入式系统语言;它非常适合应用服务器、中间件、云基础设施和相关工作。Rust 更适合嵌入式工作。在各自的领域中,两者都是领先选项。
Rust 更接近硬件并提供更强的性能保证。Scala 是垃圾回收的,所以它有回收暂停,尽管现代收集器能力很强且暂停可以很小。垃圾回收运行时也需要大量内存预算才能快速运行;Rust 可以在少得多的内存中运行,这使其更适合嵌入式系统。
Odersky 认为 Rust 有时在更高层次被过度使用,而垃圾回收器完全足够。在内存管理周围写作可能是一种智力练习,但如果可用内存允许垃圾回收器,使用一个通常会使程序更简单。相比之下,Go 被有意设计为一种很小的语言。泛型是一个重要的补充,但它在允许的内容上仍然有限。这种限制也创造了一种统一的风格和文化,使阅读另一个人的程序和进入新代码库变得更容易。
Scala 目前不支持在生产中关闭垃圾回收。关于使用自定义分配器的研究存在,类似于 Zig 的精神,但当引用可能泄漏并稍后指向未定义内存时,被回收的内存是危险的。Scala 有使用类型系统跟踪引用并静态防止泄漏的工作,但它还不是生产功能。目前,Scala 程序员应该使用垃圾回收器。
对于 Rust 与 Zig,Odersky 发现 Zig 的编译时内联构造特别干净和强大。Rust 有宏,相比之下他认为更麻烦。Scala 有一个相关的内联功能,限制是内联不得引入额外的类型错误。C++ 模板说明了为什么那个限制很重要:扩展可能产生极其复杂、难以调试的类型错误。内联意味着,在代码生成之前,编译器用函数体替换函数调用,并可以优化所揭示的实现。与可选优化器决定不同,所需的编译时内联可以被依赖。
动态语言、Python 和 Scala 的影响
主持人: 你欣赏哪些动态类型语言,Python 与 Scala 相比如何?
Martin Odersky: Python 无处不在,有愉快的语法,通常产生易于阅读的程序。Scheme 是另一种有趣的语言,因为它紧密扎根于计算机科学理论和 lambda 演算,尽管 Python 受欢迎程度高得多。
Python 和 Scala 之间的差距正在缩小。Python 现在有可选的类型语法和几种类型检查器,最近版本添加了模式匹配等功能。更一般地说,语言正在收敛于具有函数式编程深层根源的共同功能集:模式匹配、强类型系统、泛型、多态和闭包。
Scala 相对于 Python 的主要优势是其强类型系统始终处于活动状态。它可以保证某些不良状态不会发生。Python 的类型由单独的工具进行语法检查,保证更少,生态系统历来对类型强调较少。Scala 3 在语法上比早期 Scala 版本更接近 Python,所以人们可以将其视为具有不同运行时的强类型语言。Python 的巨大优势也是它作为胶水语言的角色,特别是与 NumPy 和 pandas 等高性能 C++ 库的高效链接。
历史上,Scala 融合了 Java、OCaml 或 Standard ML 以及 Haskell。Odersky 从命令式程序员开始,在 Pascal 和 Modula 中接受训练,然后成为仔细研究 ML、OCaml 和 Haskell 的函数式程序员。在 Scala 之前,他与 Philip Wadler 一起开发了名为 Pizza 的前身语言。目标是 JVM 上易于访问的函数式语言。编写 Java 编译器教会了他很多关于 Java 的知识;尽管他最初对它不屑一顾,但他开始看到它的实用价值。Scala 从 Java 继承了想法,从 OCaml 继承模块和组件,从 Haskell 继承了许多标准库约定。
编译器和 Scala 在 Twitter 的采用
主持人: 为什么编译器很难构建?
Martin Odersky: 编译器很复杂,因为它们必须同时满足许多需求。语言已经是复杂的工件;程序员期望类型推断发现有意义的类型、高效的机器代码和快速编译。调和所有这些期望需要大量工作。编译器在某些方面也比一些分布式系统容易:它们本质上是确定性的。给定相同的源,编译器应该给出相同的输出,所以失败可以被重放和调试。
Odersky 早期的 Java 编译器 Espresso,花了大约三个月的兼职工作,但它非常简单。编译器在十年或二十年内随着需求积累变得更加复杂。他用一个库来组装所需格式的字节码,但其余的都是自己写的。
主持人: Twitter 如何采用 Scala 这种当时晦涩的语言?
Martin Odersky: Twitter 最初用 Ruby 编写,可靠性很差,部分原因是垃圾回收和内存管理问题。它是一家小公司,其董事会和投资者敦促它采用 Java 作为可靠的选择。一些工程师想要更富有表现力的语言,了解 OCaml。Scala 类似于 OCaml,同时运行在 JVM 上,所以他们可以准确地说他们使用 Java 的平台和字节码。Twitter 的采用具有影响力,因为该公司受人钦佩;其他组织,通常来自 Ruby、PHP 或 JavaScript,跟随了。
AI 生成代码、类型和能力
主持人: AI 生成代码如何影响编程语言?
Martin Odersky: 我们正处于一个存在困难的时刻。AI 可以生成大量代码,而人类被要求审查所有这些,这是一项不可能完成的任务。AI 也越来越擅长发现和利用漏洞。人类失去对软件正在做什么的控制有一个真正的危险。编程语言不是完整的解决方案,但它们可以提供帮助。
如果代码是由 AI 生成的,焦点必须转移到其他地方:接口和类型。类型应该变得更强更精确,因为它们可以成为人类和 AI 之间简洁、可审查的契约。当前的类型系统通常更接近建议而不是绝对保证:强制转换、不安全内存和其他技术可以削弱它们。那些漏洞需要被关闭,因为任何漏洞都可以被利用。
人类角色必须转向需求和高级规格,将实现留给被生成的东西。一种现在可能变得核心的长期技术是能力。操作系统已经使用能力向用户和程序授予细粒度权限;代理式 AI 应该接收同样精确的权限,使人们能够知道代理无法泄漏 API 密钥、邮件或其他秘密。现有的语言还没有完全做到这一点。Scala 在这个方向上有实验性工作,Odersky 觉得这很令人兴奋。
内存安全是先决条件。允许访问未定义内存的语言不能提供可靠的保证。Rust 的内存安全是一项重要成就,更广泛的向内存安全语言推进是有用的。但仅内存安全是不够的:程序还需要控制读取权限、写入权限、对秘密的访问和其他权限。如果我们知道重写系统需要保留哪些属性,AI 可以帮助迁移用 C 和 C++ 编写的软件。
能力可以在类型系统中表达这种控制。假设代码为有限作用域接收一个文件,环境稍后会关闭它。如果没有进一步保护,代码可以存储该文件句柄并稍后返回它。能力系统可以使文件成为作用域能力,并通过类型确保它不会逃逸。如果返回的 lambda 或流保留文件,它的类型必须声明该事实;能力不能被隐藏。同样的模式适用于 arena 分配的内存和秘密:在给定时刻,程序可以限制为仅具有明确赋予它的能力。
主要的内存不安全语言包括 C 和 C++。Odersky 也认为许多其他低级语言,如 Zig 或 Nim,不是内存安全的。Rust 表明低级系统语言可以内存安全,这是许多人以前认为不可能的。然而,能力安全必须额外防止软件静默保留能力或伪造新能力。没有内存安全,这些更强的保证可以被绕过。
规格、提示词和未来工程职业
主持人: 如果机器编写更多代码,什么语言设计属性将很重要?
Martin Odersky: 人类可能有时仍会阅读生成的代码,但他们可能不会直接编写它。输入语法快捷方式的便利性不那么重要。继续重要的是说什么程序应该做什么和什么不能做的高级手段。
这可能是形式验证的强劲时代:规格可以是精确的,AI 可能有助于完成两者:满足这些规格的程序和证明它们满足这些规格的程序。但形式规格通常缺失,编写它们可能和编写程序本身一样困难。向代理的自然语言指令将保持重要,尽管它们应该变得更正式和持久。
今天,提示词可能很强大,但经常被丢弃或埋在聊天历史中。Odersky 希望提示词成为程序中的一等值:它们应该说明程序是关于什么的,保留来源,并让 AI 理解增量变化。如果开发者改变了一个细节,系统应该做一个小的相应代码改变,而不是让非确定性模型重新生成整个程序并干扰已经审查过的工作。
十年后,Odersky 期望更少的软件工程师,但标准更高的职业。工程师将需要更多的逻辑和数学来保持 AI 系统在正确轨道上。他将这个角色比作工厂控制工程师:可能更少的人直接执行工作,但指导机械的人需要更深入的专业知识。
学习建议、学术界和 Scala 的经验教训
主持人: 程序员应该学习哪些语言来拓宽思维?
Martin Odersky: 学习一种系统语言来理解硬件和软件如何连接到它。他在 C 和 Rust 之间纠结:C 简单且接近机器,而 Rust 需要学习更多抽象但内存安全。他会从 C 开始理解基础知识,然后转向 Rust 进行系统编程职业。他也推荐学习一种具有验证或证明背景的语言,如 Lean 或 Rocq,因为它磨练关于程序正确性意味着什么的直觉。Scala 有用地坐落在这些世界之间,结合表达性编程与相对可证明的强类型。
他发现特别有价值的技术书籍是 计算机程序的构造和解释,MIT 使用的一本入门文本。他的一些在 Coursera 和 EPFL 教的课程借鉴了它的材料,同时使用强类型而不是动态类型语言。
Odersky 选择学术界是因为他学习结束时的研究项目让他意识到从事答案未知的问题工作是多么令人信服。学术界的核心优势是长期独立性。行业职位有时可能薪酬高得多,但其相关性和安全性可能快速变化。大学终身教职让研究人员可以定义长期议程,尽管它也需要获得资助和说服学生。他发现与学生一起工作很有收获,对那个职业选择感到满意。
Scala 最初是结合面向对象和函数式编程的实验。技术上,他认为它是一个重大成功;生态系统方面,它面临文化挑战。这种语言汇集了有不同期望的社区。回想起来,他认为 Scala 可能更渐进地引入函数式功能。Scala 可以表达大部分 Haskell,但那也使其易于过度抽象。复杂的抽象在负责任地使用时是有价值的,但开发者可以使代码比简单 map 或直接解决方案复杂得多。
库强烈影响程序员表达系统的方式。来自 Haskell 传统的著名 monadic 框架可能导致团队相信整个 Scala 应用程序应该遵循那种风格。Odersky 对他自己的 Scala 程序选择这种风格有保留。大多数社区建议是明智的,Scala 有很多成功案例,但团队仍可能采用一种后来对他人难以理解或维护的抽象密集风格。
他也希望 Scala 少依赖 JVM 一些。Java 的通用对象方法如 toString、equals 和 hashCode 是方便的,但 Rust 或 Haskell 中更讲究的设计使用类型类来明确说明哪些类型支持相等性或哈希。这种方法设置更乏味,但可能更安全。
Odersky 没有想到 Scala 会变得像它那样重要。最初它可能只有他组外的几个用户。它的成功来自于充当动态语言(可能是慢的或崩溃的)和当时繁琐刻板的静态类型语言(如 Java)之间的桥梁。类型推断给了 Scala 动态语言的感觉,同时保持了强平台的稳固性。许多后来的语言采用了相关功能,但 Scala 是最早将它们汇集在一起的语言之一。
回顾过去,他给年轻自己的建议是冒险并勇于进取。不要简单地跟随主流:如果一个技术上狂野的想法吸引你,腾出时间去追求它。警告是保持脚踏实地,但基本建议是做一个不墨守成规的人。