CodingTour
为什么人们不断创造新的编程语言?

前言

在软件开发的历史上,我们见证了无数编程语言的诞生,从早期的机器语言到现代的高级语言,每一种语言的诞生都伴随着特定的目的和需求。近年来,我们看到了 Kotlin 对 Java 的挑战、Swift 对 Objective-C 的替代,以及 Mojo 对 Python 的创新。本文将探讨人们为何不断创造新的编程语言,以及这些新语言如何适应时代的变迁。

人们为何不断创造新的编程语言

第一个不易忽视的是已有软件与语言的局限性。

拥有大量的已有软件最能体现一门编程语言的价值,说明它的生态辽阔、普及度高。但也是负担,你不可能在不破坏这些软件稳定性的情况下对一种语言进行太多的改变,这些软件要长期维护,重写所有这些遗留软件的成本非常昂贵。因此对语言的改进不可避免地受到限制,难以适应新的开发需求。

第二点是关于语言的演化与新生。

如果你对一种语言进行过多的改变,它就不再是同一种语言了,它实际上是一种新语言,在这种情况下,创建一种新语言,摆脱旧语言的包袱,成为了一种更明智的选择。反之,如果你保留所有的包袱,最终可能会得到一种怪物语言,这里就不举例了。历史上也有从旧到新(经过重大更改)的成功案例,实际上已成为两种不同语言的语言,包括 Python(2 vs 3)、Perl(5 vs 6)和 PHP(5 vs 7),它们经过更改后仍然不是最理想的形态,但出于实际考虑,它们必须有所保留。

第三点是关于语言设计的进化。

随着时间的推移,我们对编程语言设计的理解也在不断深化。从零开始设计新的语言比改造旧语言更为容易,能够创造出更干净、更易用的语言,这种设计上的进化,为程序员提供了更愉快的编程体验。它不是语法上的迭代,不是增加了原先没有的范式,而是编程语言设计的艺术,程序员也是人,他们把设计编程语言看作是一种创造性活动,基于个人兴趣和对语言设计的独特见解,表达自我,因此每个人的作品都是独一无二的。

第四点是关于新语言的驱动力。

近两年新的编程语言有一个趋势,它们不仅是为了改进现有语言的不足而诞生,而是迎合特定的领域,提供更专业化、场景化的解决方案,如针对数据科学或系统编程的领域特定语言,这些专用语言可以为特定任务提供更定制的解决方案和更好的性能。同时在新语言的背后,促进新的社区形成,它们互相促进,共同丰富了专业领域的生态系统,包括库、框架和工具。

比如云计算和人工智能的兴起,催生了 UnisonMojo,它们要么可以针对现代硬件架构进行优化,要么利用新的编程范式,或者为并发和分布式计算提供更好的支持,总而言之,它们更适应新兴的技术需求,同时也为有兴趣实验新思想的人提供平台。

新语言如何适应时代的变迁

除了上述四点感性上的认知,我们具体看下两个案例:Mojo/Python、Swift/Objective-C。

Mojo:Python 的超集与 AI/ML 的优化

Mojo 是 Modular 公司创建的编程语言,旨在统一和简化 AI/ML 整个技术栈的编程,包括 GPU、TPU、FPGA 等多种加速器。Mojo 的目标是成为 Python 的超集,因此它与现有 Python 代码和库完全兼容,能紧密集成到 Python 生态系统中。

为什么 Mojo 不像 Python2 到 Python3 的路径那样,而是选择开发一种全新的语言来试图替代 Python 呢?有两个主要的根本原因:

  1. Mojo 要解决 “两个世界” 问题 — Python 不适于系统编程,但它作为一种通用用途的语言具有很强的粘合能力,可以与 C、C++ 在低级层面交互,在性能敏感场景中,你能用 C、C++ or 其他语言构建高性能库,这正是 numpy、TensorFlow 和 PyTorch 以及其他生态系统中大量库选择的实现方式。但尽管有这种构建高性能 Python 库的方法,你也要付出代价:构建这种混合库的过程非常复杂,需要理解 CPython 内部原理,需要掌握 C、C++ 等编程知识(从而削弱了使用 Python 的初衷)。因此,实际上构建大型的、高性能的库非常困难。这导致了动态 Python 世界和静态高性能世界的割裂。
  2. Mojo 也旨在解决 AI/ML 领域的 “N 个世界” 问题 — “两个世界” 的问题在 Python 生态中普遍存在,但对于机器学习框架的开发者来说情况更加糟糕。这些年人工智能发展迅猛,但要驱动加速器还得使用定制的编程语言,比如 CUDA,虽然 CUDA 是 C++ 开发的,但它有自己特殊的规则和限制,并且没有成熟的调试器和分析工具,它实际上被锁定在单个硬件制造商的生态内,这导致了严重的碎片化。人工智能在硬件方面拥有令人难以置信数量的创新成果,因此复杂性也在失控地螺旋上升,现在已经有许多异构编程框架(OpenCL、Sycl、OneAPI 等),复杂度爆炸趋势还在继续上升。

Why Mojo🔥

以及,Modular 希望 Mojo 能解决这些更具体的问题:

  1. 能将 Python 的动态编程模型与高性能系统代码统一起来。Python 依赖于 CPython 实现,这给 Python 带来了根本性的限制,比如那把大锁(GIL)使其无法利用多线程。改善 CPython 只能在一定程度上缓解这些问题,无法从根本上解决,这是 Python 基础设施的局限性
  2. 能提供一种统一的语言来编程不同加速器,避免使用单独的语言如 CUDA,以此简化 AI/ML 整个技术栈的编程。现有的编译器技术如 LLVM、GCC 并不适合(并且基于它们的任何语言和工具也不行),尽管它们支持许多 CPU 和一些常见的 GPU,但这些编译器技术是几十年前设计的,无法完全支持现代芯片架构
  3. 独立于 CPython,获得 Python 所缺乏的高性能、控制和系统编程能力
  4. 借鉴 Rust、Swift 等语言以及 Clang 编译器的经验,在保持 Python 兼容性的同时逐步将生态系统迁移到新语言
  5. 必要时也可以脱离 Python,例如添加更适合系统编程的原语/范式

我们能看到 Mojo 做了 2 件在 Python 里完全不敢想象的事情:

  1. 统一了 “两个世界”
  2. 围绕 MLIR,从头设计了新的编译器和运行时基础设施,因此 Mojo 也是第一种主要面向 MLIR 的语言,在编程 AI 加速器方面有独特优势

为什么 MLIR 这么重要?

它是 LLVM 和 Swift 的创造者,Chris Lattner 的又一力作,号称下一代编译器技术,它的优势在于:

  • 面向多种硬件加速器,包括 AI ASICS、量子计算系统、FPGA 等,传统的 LLVM 等编译器技术主要面向通用 CPU/GPU,难以高效利用这些新型加速器
  • 分层式中间表示,高层 IR 接近输入语言,底层 IR 接入硬件,中间有多个变换层,这种设计使编译器前后端解耦,有利于支持多种语言和硬件目标
  • 编译过程可见,与传统编译器黑盒式流程不同,MLIR 的编译过程完全可见和可控,包括中间代码在内,有利于编译器自身的演进和优化
  • 强大的可扩展性和定制性,允许构建专门的编译器方言
  • 尽管 MLIR 是一个较新的项目,但已得到谷歌、ARM、英特尔、AMD 等多家公司及机器学习、量子计算等领域的支持,有望成为新一代的编译器基础设施标准

虽然也有许多其他项目在使用 MLIR,但 Mojo 是第一个从一开始就针对 MLIR 设计的主要语言,可以释放 MLIR 的全部潜力,高效编译到各种新型硬件加速器上运行,这是它与其他语言的一大区别所在。

总的来说,Mojo 是为了从根本上突破 Python 自身架构的限制,并借鉴了现代语言(如 Rust、Swift)和编译器基础设施的优势,以更好地服务于 AI/ML 等高性能计算领域,而不是走 Python 自身渐进式改进的路线。

Swift:Apple 的语言革新

代入一下,Apple 想要的语言需要具备哪些特征?

首先,它必须是一种编译语言,而不是解释语言或依赖于运行时 pcode 执行引擎的语言,这就排除了很多语言,Ruby、Python、Java、JavaScript 等都是解释或基于 pcode 的。

还是一种函数式语言,或者至少是一种对函数式编程有原生支持的语言,这也是 Objective-C “过时” 的主要原因之一。几年前,Apple 已将 Objective-C 发展为能更好地支持函数式编程,实际上,同期的其他语言也在做类似的事情(C、C++、Java、Python 等都在增加对闭包和匿名函数的支持),但这只是一部分,他们想要的远不止这些,Apple 于 2019 年发布了 SwiftUI,于 2014 年发布 Swift,也许 SwiftUI 才是 Swift 诞生的原因。

同时,不同于 Objective-C,新语言应该是一种语法轻巧、易学易用,既适应命令式编程又适应函数式编程,并直接支持面向对象等概念的语言。这又排除了大多数现有的函数式语言,Lisp 类似的语言没有轻巧且受命令式或面向对象语言经验丰富的程序员喜欢的语法;基于 Hindley-Milner 类型系统的语言可能有也可能没有优秀的语法(可以说 Haskell 拥有所有语言中最优雅的语法),但它们不支持名义子类型

它必须是强静态类型的,以支持更好的优化和错误处理。这有两种哲学,强类型支持者强调完全排除一个或多个错误类别的优点以及编译器优化的潜力;鸭子类型支持者强调更快的开发速度和更简单 — 我们不讨论正确性。

相比动态性,Apple 更强调强类型、更少的程序潜在错误,以及更多的优化空间。

还有一点不容忽视,Apple 需要能完全定义、掌控它。它不能是由一个决策过程缓慢的委员会定义的语言,要么是 Apple 完全控制,要么是开源定义的语言,不是语言的实现,那不重要,他们需要能够 “改变” 语言的定义,这才是能决定语言本身是什么的东西。这个做法和微软推出 C# 的原因(之一)很像,区别在于,微软要的是 “向后兼容”,即新的东西必须完全支持已经存在的东西。而 Apple 相信演变,新的东西可能会 淘汰/杀死 已有的东西,所以任何已有的东西可能需要适应新的才能生存。

最后,它必须要能与动态类型(动态且无类型)的 Objective-C 互操作。Objective-C 极其动态,它是面向对象的,但可以在运行时创建新的 Class,甚至可以向现有 Class 添加新方法(还有新成员),即使你无法访问类的源代码也能做到这一点。实际上,甚至无需修改定义 Class 或使用它的现有二进制(编译后的)代码,也可以在运行时进行。你可以在运行时检查代码并了解有哪些类以及它们有哪些方法可以用,完全不依赖源代码。

没有一种语言能完全符合要求,Swift 是 Apple 唯一的答案。

最后

从 Swift 到 Mojo,它们都在其特定的领域内展现出了独特的优势和潜力,每一种新语言的诞生,既是技术进步的必然,也是承载着开发者的梦想和创造力,它们不是冰冷的代码,是人类对于更高效、更人性化编程体验的追求。我们能预见到未来将有更多的新语言诞生,应该保持期待,这是能让编程成为更加美好和富有创造性的活动。