从第一性原理出发:为什么选择 Scala?
Scala,自 2004 年问世以来,既非编程语言领域的元老,亦非新秀。本文将探讨 Scala 所提供的独特功能组合,并将其与市场上的其他语言进行比较,深入挖掘语言的本质。通过此文,您将了解为何考虑将 Scala 纳入您的编程工具箱,作为一项宝贵的补充。
Scala 网站将 Scala 的宣传总结如下:
Scala 将面向对象和函数式编程结合在一个简洁的高级语言中。Scala 的静态类型有助于避免复杂应用中的错误,其 JVM 和 JavaScript 运行时使您能够轻松构建高性能系统,并能方便地访问庞大的生态系统库。
Scala 是一种语言,它提供了大多数程序员都会欣赏的独特功能组合。它结合了编译语言的性能和大规模可维护性、Java 语言和虚拟机的工具和生态系统,以及通常与脚本语言相关的简洁性和易用性。
Scala 过去曾面临一些挑战——编译速度缓慢、库和框架令人困惑,以及一个更注重炒作而非实际工作的社区。但在过去的五年中,Scala 生态系统成功克服了许多长期存在的问题,蜕变为一个更简洁、更高效的环境,非常适合高效完成任务™。
在这篇文章中,我们将首先讨论程序员在使用 Scala 时可能欣赏的用户层面卖点,深入探讨使 Scala 语言成为其本质的基本原则,最后通过比较这些原则与其他您可能考虑用于下一个项目的编程语言,来结束本文。
Scala 语言的用户端卖点
在本节中,我们将讨论 Scala 语言的一些面向用户的功能。
一种编译型语言,却拥有动态语言的体验
Scala 是一种能够很好地从一行代码片段扩展到百万行生产代码库的语言,兼具脚本语言的便利性和编译语言的性能与可扩展性。Scala 的简洁性使得快速原型开发变得轻松愉快,而其优化编译器和快速的 JVM 运行时提供了出色的性能,支持您最繁重的生产工作负载。无需为每个用例学习不同的语言,Scala 让您能够重用现有技能,从而将注意力集中在实际任务上。
编译型语言与脚本语言之间的二分法在软件行业中无处不在。乍一看,权衡如下:
编译型语言 | 脚本语言 |
---|---|
C++, Java, C#, ... | Python, Ruby, JS, ... |
冗长 | 简洁 |
卓越性能 | 性能不佳 |
静态类型 | 动态类型 |
出色的 IDE 和工具支持 | IDE 和工具支持不佳 |
重型构建配置 | 最小化或轻量级构建配置 |
在小程序中不方便 | 小程序中方便使用 |
在大程序中可管理 | 在大规模程序中难以管理 |
作为程序员,选择哪种语言总是一个难题:几乎每个大型程序都始于一个小程序!通常很难判断哪些小程序会发展壮大,哪些永远不会。很多时候,我们花费大量时间和精力用编译型语言开发小程序,但它们从未发展到足以让编译型语言的开销物有所值;或者用脚本语言开发大型程序,因为它们随着时间缓慢增长,超出了脚本语言适合的规模。
混合语言
存在第三类语言:提供类型推断、便捷库和其他提升生活质量改进的编译语言,这些特性模糊了编译语言与脚本语言之间的界限:
编译型语言 | 脚本语言 | 混合语言 |
---|---|---|
C++, Java, C#, ... | Python, Ruby, JS, ... | Scala, F#, Kotlin, ... |
冗长 | 简洁 | 简洁 |
卓越性能 | 性能不佳 | 卓越性能 |
静态类型 | 动态类型 | 静态类型并带有类型推断 |
出色的 IDE 和工具支持 | IDE 和工具支持不佳 | 出色的 IDE 和工具支持 |
重型构建配置 | 最小化或轻量级构建配置 | 最小化或轻量级构建配置 |
在小程序中不方便 | 小程序中方便使用 | 方便小型程序 |
在大程序中可管理 | 在大规模程序中难以管理 | 在大程序中可管理 |
Scala 是一种“混合”语言,它融合了编译语言和脚本语言的特点,旨在为您提供两者的最佳特性。您可以在交互式 REPL(如 Ammonite)中用一行代码开始编写有用的 Scala 代码:
@ requests.post(
"https://api.github.com/repos/lihaoyi/test/issues",
data = ujson.Obj("title" -> "hello"),
headers = Map("Authorization" -> s"token $token")
)
如果你想保留代码,可以将其扩展为一个 Scala 脚本:
#!/usr/bin/env amm
val token = os.read(os.pwd / "github-token.txt")
requests.post(
"https://api.github.com/repos/lihaoyi/test/issues",
data = ujson.Obj("title" -> "hello"),
headers = Map("Authorization" -> s"token $token")
)
./script.sc # run the script
在将其扩展为一个使用构建工具(如 Mill)的项目之前,提供增量编译、测试、打包以及其他随着项目扩展到数十万行代码而变得越来越必要的功能。
为什么要使用混合语言?
在传统的编译型语言与脚本语言的世界中,这三种用例很可能会是截然不同的语言:
- 交互式使用:Bash
- 短脚本:Python 或 Ruby
- 大型系统:Java 或 C++
在这些语言之间进行转换通常需要昂贵的重写。何时进行这样的重写总是一个令人头疼的问题。我们都见过那些变得过于庞大、用 Python 重写会更好的 Bash 脚本,或者那些变得过于庞大、用 Java 或 C#等编译语言重写会更好的 Python 应用程序!
像 Scala 这样的轻量级编译语言旨在解开这一戈尔迪之结:你可以在交互式 REPL 中使用与编写百万行后端分布式系统相同的语言编写第一行代码,以及介于两者之间的所有内容:
- 交互式使用:Scala
- 短脚本:Scala
- 大型系统:Scala 在小规模应用中,Scala REPL 和脚本与 Python 或 Ruby 一样方便使用,而大规模的 Scala 系统在可维护性和性能方面与用 Java 或 C# 编写的大型系统不相上下。
Scala 并非此类别中的唯一语言:F# 也算其一,而 Swift 和 Kotlin 则介于混合型与更传统的编译型语言之间。但根据我的经验,Scala 在很大程度上成功打破了这种二分法,提供了一种能够从最小的即用即抛的一行代码扩展到最大且最重要的生产系统的语言。
安全和正确性简易指南
cala 的函数式编程风格和类型检查编译器有助于排除整类错误和缺陷,节省您的时间和精力,使您能够专注于为用户开发新功能。与其在生产环境中与
TypeError
和NullPointerException
作斗争,Scala 在编译阶段就提前暴露错误和问题,让您在它们影响业务之前就能解决。部署代码时,您可以充满信心,不必担心因愚蠢的错误或小问题导致的宕机而半夜被叫醒。
Scala 的一个重点是确保您编写的代码的正确性。
任何软件工程师都知道,编写“一个程序”并不是他们工作中最困难的部分。难点在于确保程序的正确性。不仅要在预期接收的所有输入下按照你的想法运行,还要在代码随时间演进并由众多不同人员维护时保持正确。
Scala 通过两种方式帮助解决这个问题:
- 静态类型检查:Scala 编译器允许你在程序运行之前检查许多“琐碎”的错误,防止愚蠢的错误悄悄溜入,让程序员能够专注于更微妙和复杂的问题
- 函数式编程:一种设计应用程序的风格,使得更容易推理应用程序的工作原理以及如何进行修改。
我将依次讨论每种方法。
静态类型检查
静态类型检查是指在程序运行之前,通过分析代码来发现问题。在编译型语言中,这通常是编译器本身的工作,但在其他语言中可能并非如此:例如,Python 语言通过 MyPy 提供了这一功能,MyPy 是一个可以随时运行的独立检查器,但并不是执行代码的必要条件。
静态类型检查是一个常引发宗教式辩论的话题。在小规模范围内,这通常取决于个人偏好:有些人偏爱静态类型,而有些人则喜欢无需也不允许类型检查的动态语言。通常,静态类型语言比动态语言更冗长,因为需要在代码的各个部分添加类型注解,尽管像 Scala 和 F#这样的混合语言通过类型推断减少了这种冗长性。
在小规模情况下,静态类型检查的个人偏好并不重要。在较大规模下,缺乏静态类型检查对于维护系统的人来说是一个巨大的痛点。虽然这在过去并不明显,但如今我们可以看到每种动态语言都在增加对静态类型的支持:
- Python 的 MyPy(由 Dropbox 和 Python 核心开发者开发)
- Ruby 的 Sorbet(由 Stripe 开发)和 RBS(由 Ruby 核心开发者开发)
- JavaScript 的 TypeScript(由微软开发)
- PHP 的 Hacklang(由 Facebook 开发)
如果大型公司没有从中看到价值,他们就不会每年花费数百万美元(相当于 5 到 10 个硅谷全职软件工程师的薪资)为他们的动态语言开发类型检查器。事实上,一旦达到一定规模,动态语言中缺乏静态类型检查往往会导致生产力损失数百万美元,以至于这项支出是值得的。
另一个有趣的数据点是来自 Rollbar 错误日志记录公司的“十大 JavaScript 错误”实证列表:
1000 多个项目中的前 10 大 JavaScript 错误
如下所示:
- Uncaught TypeError: Cannot read property
- TypeError: ‘undefined’ is not an object
- TypeError: null is not an object
- (unknown): Script error
- TypeError: Object doesn’t support property
- TypeError: ‘undefined’ is not a function
- Uncaught RangeError
- TypeError: Cannot read property ‘length’
- Uncaught TypeError: Cannot set property
- ReferenceError: event is not defined
10 个最常见的 JavaScript 错误中的 8 个,即(1) (2) (3) (5) (6) (8) (9) (10),都是静态类型检查器通常能捕捉到的问题。无论开发者多么努力地维护 JavaScript 代码的质量,仍有大量“低级”类型错误在生产环境中出现。难怪 TypeScript 能迅速崛起并变得如此流行!
虽然有时会讨论静态类型或单元测试哪种方法更好,但实际上并非非此即彼。静态类型检查补充了单元测试,对经过良好测试的代码库的益处与对测试不足或未测试的代码库的益处一样大。与代码审查、持续集成、函数式编程(我将在下文讨论)及其他方法论一起,这些手段共同作用,增强了你对软件正确性的信心。
即使对于没有数百万预算的小公司,如果静态类型检查已经可用,将其添加到语言中通常也是值得的。在 Scala 中,静态类型是内置的,无需额外成本。
函数式编程
简而言之,“函数式编程”是一种专注于函数(即接受参数并返回计算值)的编程方式,而不是改变字段和变量。这使得更容易看出一段代码需要什么(它的函数参数)以及它做什么(它计算的值),从而更容易理解、重构或向代码库添加新功能。
如需更详细的讨论,请查看以下帖子:
函数式编程并非 Scala 独有,Scala 也不是在函数式编程方面投入最深的语言。尽管如此,Scala 却有着鲜明的函数式风格:内置集合默认是不可变的,代码通常侧重于值的转换而非变量的修改,标准库和生态系统中有许多调整,使得这种函数式风格既方便又高效。
如今,大多数语言都在引入函数式编程特性:
- C# 正在引入结构模式匹配和记录
- Java 正在引入结构模式匹配和记录
- Python 正在引入结构模式匹配,并且已经拥有记录(数据类)
语言正在引入函数式特性,因为核心开发者发现它们是对现有面向对象和过程式风格的绝佳补充。在 Scala 中,函数式编程一直是语言和生态系统的重要组成部分,与更多人可能熟悉的面向对象风格并驾齐驱。大多数团队在适当的地方平衡使用这两种方法。因此,Scala 开发者通过函数式编程的优势,帮助他们组织代码以解决困难、复杂的问题。
一个广泛而深入的生态系统
作为运行在 Java 虚拟机上的语言,Scala 能够访问庞大的 Java 生态系统中的标准库和工具,这些是你构建生产应用程序时不可避免需要的。无论你是在寻找 Protobuf 解析器、机器学习工具包、数据库访问库、用于查找瓶颈的分析器,还是用于生产部署的监控工具,Scala 都具备将你的代码推向生产所需的一切。
Scala 最后一个主要的面向用户的卖点是其生态系统的丰富性。在生产环境中编写代码时,仅有一个编程语言的编译器或解释器是不够的。在典型的一周中,我会使用:
- 数百个第三方库,涵盖了语言本身未内置的各种功能。
- 一个包仓库,供用户发布和分享他们的代码
- 构建工具以增量编译您的代码或并行执行编译
- 一个分析器,用于调查和解决性能问题
- 监控工具,用于在代码部署到生产环境后进行监控
Scala 具备所有这些特性,部分得益于其自身生态系统的优势,部分得益于其依托的 Java 生态系统的优势。
Maven 中央仓库
Maven Central Java 包仓库是世界上最大的包仓库之一,提供了开源库,几乎可以实现任何人想到的任何功能。例如,如果我突然需要操作 PDF 文件,我可以从 Maven Central 引入 Apache PDFBox,然后就可以开始工作了:
import $ivy.`org.apache.pdfbox:pdfbox:2.0.18`
val outPath = os.pwd / "combined.pdf"
val pdfFiles = os.list(os.pwd / "inputs")
val merger = new org.apache.pdfbox.multipdf.PDFMergerUtility
for (pdf <- pdfFiles merger.addSource(pdf.toIO)
val out = os.write.outputStream(outPath)
try {
merger.setDestinationStream(out)
merger.mergeDocuments()
} finally out.close()
分析器与监控
像 Java Flight Recorder 这样的分析器允许在生产环境中进行低开销的性能监控,而其他工具如 JProfiler 或 Yourkit 则可以快速应用,立即提供对应用程序性能特征的深入了解:
构建工具
构建工具如 SBT、Mill、Gradle、Maven 或 Bazel 提供了丰富的项目管理选项,各有不同的权衡以适应您的需求。
许多人力数十年的工作已经投入到 Scala/Java 生态系统的每一个方面,而使用 Scala 的开发者可以免费从中受益。
Scala 语言的基本原理
上一节讨论了 Scala 的独特特性如何直接使最终用户受益。现在我们将介绍使这一切成为可能的 Scala 核心原则。
全情投入静态分析
Scala 相较于大多数语言更注重静态编译和分析。几乎所有 Scala 语言特性都是在静态时解析的,甚至像“猴子补丁”式的扩展方法和隐式转换也是在编译时实现的,而非运行时。即使与其他编译型语言如 Java 相比,Scala 也倾向于避免 Java 代码中常见的运行时反射,转而采用隐式和其他编译时特性。
尽管在编译时完成任务有时可能很复杂,尤其是像隐式转换和宏这样的语言特性因其复杂性而闻名,但这种方法仍然具有一些根本性的优势。
为什么使用静态分析
从根本上说,静态编译和分析意味着计算机可以在编译时对代码进行更多的推理,而不是在运行时动态处理。
这意味着:
- 与大多数编译型语言类似,Scala 由于代码的静态特性,使得编译器(或即时编译器)更容易进行优化,因此其运行速度比动态脚本语言快一个数量级
- 像 IntelliJ 这样的 IDE 能够精确且准确地分析 Scala 代码。跳转到定义、查找用法、重构等功能在静态类型语言中比在动态语言中效果更好。
- 编译时生成诸如 JSON 序列化器之类的东西具有出色的性能,而基于运行时反射的实现往往非常缓慢。这在每种语言中都适用,无论是静态语言还是动态语言。
- 编译器能够推断代码能做什么、不能做什么,并轻松实现诸如将您的 Scala 代码编译为高性能的 JavaScript(通过 Scala.js),或使用 Graal Native Image 编译为静态二进制文件,且保持完美兼容性。相比之下,动态语言(如 Clojure)的跨编译器通常会有一长串的注意事项和不兼容问题。
一般来说,围绕静态编译和静态分析的语言意味着计算机可以在你进行任何操作时提供更多帮助:无论是查找变量的使用情况、重命名某个方法,还是仅仅试图让你的代码运行得更快。这些都是软件工程师日复一日在做的事情,拥有能够尽可能自动化这些工作的工具真是太棒了!
其他语言的静态分析
Scala 并不是唯一认识到静态编译多方面优势的语言。即使是动态语言的代表 Python,也已意识到这一点:
- 在 PEP484 中实现了类型注解
- 投资于 MyPy 静态类型检查器以发现错误
- 开始使用这些类型将 Python 编译为高性能的 C 代码
这是一个在过去多次上演的故事,我们可以预见它在未来也会多次重演。从根本上说,让计算机承担更多工作以帮助程序员理解、调试、重构或优化代码的承诺太过诱人,不容忽视。但为了实现这些收益,我们需要一种计算机能够有效进行静态分析的语言。Scala 已经是这样一种语言。
推理以提升易用性
关于 Scala 最有趣的一点是,它大量依赖推理来使事物易于使用。考虑以下使用 Scala 渲染简单 HTML 片段的代码片段:
os.write(
os.pwd / "index.html",
body(
h1("Blog"),
for (postTitle <- postTitles)
yield h2(postTitle)
)
)
没有类型推断的情况下,它看起来会是这样:
os.write(
os.pwd / "index.html",
body(
h1(new StringFrag("Blog")),
new SeqFrag(
for (postTitle: String <- postTitles)
yield h2(new StringFrag(postTitle))
)
)
虽然这两个代码片段都易于理解,但很明显,推断的 new StringFrag
和 new SeqFrag
构造函数以及推断的 : String
注解在保持代码简洁方面做了大量工作。上面的代码片段感觉像是动态语言中的内容,而下面的代码片段则明显带有企业级 Java 的风格。
如今,类型推断已成为编译语言的基本要求:像 Ruby 或 Python 这样的动态语言的新型类型检查器大量使用类型推断,甚至像 Java 这样更传统的编译语言也在朝这个方向发展。Scala 是一种从一开始就围绕类型推断构建的语言,这使得它在 Scala 中的表现比其他语言事后添加类型推断要更加连贯和设计精良。
值推理
Scala 在编程语言中或许独树一帜,因为它不仅推断类型注解(“类型推断”),还能推断构造器/转换(如上所示)或其他值。
考虑以下代码片段,它定义了一个 Example[T]
特性,并包含一个单一的值,以及一个 exampleFor[T]
函数,使我们能够获取任何 T
的值,前提是我们已经定义了相应的示例:
@ trait Example[T]{ def value: T }
@ def exampleFor[T: Example] = implicitly[Example[T]].value
我们可以使用下面的 implicit
语法为 Int
和 String
定义示例:
@ implicit object ExampleInt extends Example[Int]{def value = 1213}
@ implicit object ExampleString extends Example[String]{def value = "moo"}
@ exampleFor[Int]
res6: Int = 1213
@ exampleFor[String]
res7: String = "moo"
exampleFor[Int]
查找任何类型为 Example[Int]
的 implicit
值,找到 ExampleInt
,并返回值 1213
。对于 exampleFor[String]
也是如此,到目前为止一切顺利。
当您定义依赖于其他 Example
的 Example
时,这里就变得有趣了,例如:
@ implicit def ExampleTuple[T: Example, V: Example]: Example[(T, V)] = new Example[(T, V)]{
def value = (implicitly[Example[T]].value, implicitly[Example[V]].value)
}
在这里,我们定义了一个 Example[(T, V)]
,或者说是 2 元素元组的一个示例,适用于已经定义了 Example
的任何类型的元素。现在,我们不仅可以请求 exampleFor[(Int, String)]
或 exampleFor[(String, Int)]
:
@ exampleFor[(Int, String)]
res9: (Int, String) = (1213, "moo")
@ exampleFor[(String, Int)]
res10: (String, Int) = ("moo", 1213)
我们也可以请求深度嵌套元组的 exampleFor
,只要它们由 Int
、 String
和二元素元组组成:
@ exampleFor[((String, String), (Int, Int))]
res11: ((String, String), (Int, Int)) = (("moo", "moo"), (1213, 1213))
@ exampleFor[((String, Int), (Int, String))]
res12: ((String, Int), (Int, String)) = (("moo", 1213), (1213, "moo"))
@ exampleFor[((String, Int), (((Int, String), Int), String))]
res13: ((String, Int), (((Int, String), Int), String)) = (
("moo", 1213),
(((1213, "moo"), 1213), "moo")
)
虽然大多数编程语言允许你从表达式 (1213, "moo")
推断类型 (Int, String)
,但 Scala 允许你反其道而行之:从类型 (Int, String)
推断出一个值 (1213, "moo")
!这在各种不同的场景中都非常有用,使得 Scala 语言既能像动态语言一样简洁,同时仍然保留其静态类型的特性。
按我理解行事
能够指定所需值的类型,并让编译器自动生成提供该值的代码,这是 Scala 独有的特性。Scala 实现这一点并非通过巧妙的技巧或革命性的 AI,而是通过一套定义良好的规则,规定了程序在何处可以推断类型和值。从根本上说,这种灵活的程序推断是我们所能接近的“按我意思执行”语言的圣杯。
充分利用宿主语言
Scala 做得很好的最后一点是,它严重依赖宿主语言来实现其语言语义和实现。Scala 通常在 JVM 上运行,JVM 与 Java 生态系统一起提供了大量有用的功能,Scala 无需为此操心:
- 多线程
- 包管理和包生态系统
- 集成开发环境(IDE)和编辑器
- CPU 和内存使用情况的分析器
- 即时编译
- AOT 编译(例如使用 Graal Substrate VM)
- 调试器
- 垃圾收集器(有多种选择!)
尽管 JVM 存在局限性,但显而易见的是,Scala 从依托于 JVM 和 Java 生态系统中获益匪浅。以 Ocaml 中的多线程状态为例,多年来它一直处于“进行中”的状态:
- https://discuss.ocaml.org/t/multicore-ocaml-march-2020-update/5406
依托于 JVM,Scala 继承了一个功能完备、定义明确且经过实战检验的多线程实现。Scala 社区无需花费数十年时间重新实现多线程内存模型、线程、原子操作、锁、信号量、volatile 等,而是可以将精力集中在提升语言的易用性和人体工程学上,这些才是真正使 Scala 与众不同的地方。
如今,Scala 也可以通过 Scala.js 在浏览器中运行,这是在现有成熟生态系统之上成功引导丰富社区的另一个例子。
结论:所有语言都通向 Scala
每种编程语言都面临着相同的压力:为了更好的性能、工具、便利性、正确性或大规模的可维护性。我已经讨论过为什么我认为 Scala 不仅在表面特性上表现出色,而且在底层方法上也十分稳健。Scala 专注于帮助软件工程师解决他们最实际的挑战,并具备良好的基础,以确保社区的努力能够获得良好的回报。
编程语言种类繁多,但随着时间的推移,它们在许多方面正变得越来越单一。回想十年前,即 2010 年左右,主流编程语言的状态:
执行风格 | 类型 | 性能 | 记录 | 模式匹配 | Lambda 表达式 | |
---|---|---|---|---|---|---|
C++ | 编译完成 | 静态 | 很好 | 是的 | 不 | 不 |
Java | 编译 + JIT | 静态 | 好 | 不 | 不 | 不 |
C# | 编译 + JIT | 静态 / 推断 | 好 | 不 | 不 | 是的 |
Python | 解释型 | 动态 | 糟糕 | 不 | 不 | 是的 |
Ruby | 解释型 | 动态 | 糟糕 | 不 | 不 | 是的 |
JS | 解释型 + JIT | 动态 | 好的 | 是的 | 不 | 是的 |
Scala | 编译 + JIT | 推断的 | 好 | 是的 | 是的 | 是的 |
当时,Scala 无疑是与众不同的:它以类型推断来实现简洁性,并具备诸如记录(在 Scala 中为case class
)、模式匹配和 lambda 函数等函数式编程特性。编译型语言与解释型语言之间的界限非常明显。但随着行业的演进,到了 2020 年之后,这些语言的格局已大不相同:
执行风格 | 类型 | 性能 | 记录 | 模式匹配 | Lambda 表达式 | |
---|---|---|---|---|---|---|
C++ | 编译完成 | 静态 | 很好 | 是的 | 不 | 是的 |
Java | 编译 + JIT | 静态 / 推断 | 好 | 是的 | 是的 | 是的 |
C# | 编译 + JIT | 静态 / 推断 | 好 | 是的 | 是的 | 是的 |
Python | 解释型 / 编译型 | 动态 / 推断 | 糟糕 | 是的 | 是的 | 是的 |
Ruby | 解释型 / JIT | 动态 / 推断 | 糟糕 | 不 | 是的 | 是的 |
JS | 解释型 + JIT | 动态 / 推断 | 还行 | 是的 | 不 | 是的 |
Scala | 编译 + JIT | 推断的 | 好 | 是的 | 是的 | 是的 |
每种语言都在不断演变,这一点不应让任何人感到意外,但可能令人惊讶的是,每种语言都在朝着同一个方向发展:它们都在成为具有即时编译特性、类型推断的语言,并融入了函数式编程特性,如记录、模式匹配和 lambda 表达式。显然,这些多样化的语言在细节上差异巨大,但它们前进的方向是明确的。较新的语言如 Kotlin 或 Swift 也往往符合这一模式。
Scala 已经满足了这些标准中的大部分。无论您是在构建编程语言、网站,还是大规模分布式系统,Scala 都是一个极佳的基础,助您实现目标。
在过去的几年里,我们见证了 Ruby 和 Python 的核心开发者们为了实现 JIT 编译和静态编译以提升性能而做出的巨大努力。Java 和 C# 的开发者们也在不懈努力,使他们的语言更加符合人体工程学并易于上手。所有人都试图融入函数式编程的特性。
尽管付出了这些努力,将这些特性改造到现有语言上,总不如从一开始就设计好的语言那样贴合。Python 上嫁接的类型系统,无法与真正的编译语言相媲美的优雅或性能优势,而 Java 的人体工程学改进也只是小小的喘息,远未达到脚本语言的便利程度。尽管付出了巨大的努力,试图以这种方式迎头赶上,确实是一项徒劳无功的任务。
如果你追溯他们的努力到最终结论,已经有一种语言能够满足他们的一切需求:那就是 Scala。