拟议语言特性分类
本文档概述了为 Scala 3 提出的构造,旨在促进关于哪些内容以及何时包含的讨论。它将功能分为八组:(1) 核心基础,(2) 简化,(3) 限制,(4) 已删除的功能,(5) 已更改的功能,(6) 新功能,(7) 旨在用以替换现有宏的元编程功能,以及 (8) 类型检查和推断的更改。
每组都包含部分,对其中构造的状态(即成为 Scala 3 部分的相对重要性,以及决定此事的相对紧迫性)以及迁移成本进行分类。
当前文档反映了截至 2019 年 4 月的情况。它将更新以反映该状态的任何未来变化。
核心基础
这些新构造直接模拟了 DOT、高阶类型和 SI 演算用于隐式解析 的核心功能。
状态:必不可少
这些是 Scala 3 的核心功能。没有它们,Scala 3 将成为一种完全不同的语言,拥有不同的基础。
迁移成本:无到低
由于这些是新增功能,因此旧代码通常没有迁移成本。一个例外是交集类型,它用稍微清理过的语义替换了复合类型。但很少有程序会受到此更改的影响。
简化
这些构造用现有构造替换,旨在使语言更安全、更易于使用,并促进代码风格的统一。
- 特质参数 用更通用的构造替换 早期初始化器。
- 给定实例 替换隐式对象和 def,专注于意图而不是机制。
- 使用子句 替换隐式参数,避免其歧义。
- 扩展方法 用更清晰、更简单的机制替换隐式类。
- 不透明类型别名 替换大多数值类的使用,同时保证没有装箱。
- 顶层定义 替换包对象,删除语法样板。
- 导出子句 提供一种简单通用的方式来表达聚合,可以替换以前包对象继承自类的外观模式。
- 可变参数拼接 现在使用
*
而不是@ _*
,镜像可变参数表达式。 - 创建者应用 允许使用简单的函数调用语法而不是
new
表达式。new
表达式作为创建者应用无法使用的情况的备用方案保留下来。
除了早期初始化器和旧式可变参数拼接之外,所有被取代的构造在 Scala 3.0 中仍然可用。计划是以后弃用并逐步淘汰它们。
值类(被不透明类型别名取代)是一个特例。目前没有弃用值类的计划,因为我们可能会在 JVM 本地支持的情况下以更通用的形式将其带回来,正如 Valhalla 项目所计划的那样。
状态:双模:现在或永不 / 可以延迟
这些是必要的简化。如果我们决定采用它们,我们应该在 3.0 中进行。否则我们将面临一个尴尬的局面,即 Scala 3 文档必须描述一个将在未来被更简单的功能替换或取代的旧功能。
另一方面,我们现在只需要决定这个列表中的新功能。决定放弃被取代的功能可以延迟。当然,采用新功能而不决定放弃被取代的功能会使语言变得更大。
迁移成本:中等
在接下来的几个版本中,旧功能将继续可用,弃用和重写技术可以使任何迁移工作变得低廉且逐步进行。
限制
这些结构受到限制,以使语言更安全。
- 隐式转换:只有一种方法可以定义隐式转换,而不是多种方法,并且可能令人惊讶的隐式转换需要语言导入。
- 给定导入:隐式现在需要一种特殊的导入形式,以使导入清晰可见。
- 类型投影:只有类可以用作类型投影
C#A
的前缀C
。不再支持抽象类型上的类型投影,因为它不安全。 - 多重宇宙相等 实现了一种“选择加入”方案,以排除使用
==
和!=
进行的无意义比较。 - 中缀 使方法应用语法在所有代码库中保持一致。
不受限制的隐式转换在 Scala 3.0 中仍然可用,但将在以后被弃用并删除。列表中其他结构的无限制版本仅在 -source 3.0-migration
下可用。
状态:现在或永不
这些是必要的限制。如果我们决定采用它们,我们应该在 3.0 中进行。否则,我们将面临一个尴尬的局面,即 Scala 3 文档必须描述一个将在未来受到限制的功能。
迁移成本:低到高
- 低:多重宇宙相等排除了无意义的代码,因此其采用所需的任何重写都应归类为错误修复。
- 中等:对隐式的限制可以通过直接重写来解决。
- 高:不受限制的类型投影不能总是直接重写,因为它通常不安全。
已删除的结构
建议删除这些结构,而没有新的结构来替代它们。删除这些结构的动机是简化语言及其实现。
这些构造被弃用的日期各不相同。当前状态为
- 完全未实现
- DelayedInit、存在类型、弱一致性。
- 在
-source 3.0-migration
下支持- 过程语法、类遮蔽、符号字面量、自动应用、自动元组化(受限形式)。
- 在 3.0 中支持,将在以后弃用并逐步淘汰
- XML 字面量、复合类型。
状态:混合
目前未实现的功能需要大量的实现工作,这在大多数情况下会使编译器更容易出现错误和脆弱,并且更难理解。如果我们不决定放弃它们,它们可能会在 Scala 3.0 版本中显示为“尚未实现”。
目前已实现的功能可能会无限期地保留。更新的文档可能会简单地忽略它们,预计它们最终可能会消失。因此,关于它们删除的决定可以延迟。
迁移成本:中等至高
已弃用的功能需要重写以避免在程序中使用它们。这些重写有时可以是自动的(例如,对于过程语法、符号字面量、自动应用),有时需要手动进行(例如,对于类遮蔽、自动元组化)。有时重写必须是非局部的,影响使用位置和定义位置(例如,在 DelayedInit
的情况下,除非我们找到解决方案)。
更改
这些构造已经过更改,以使它们更规范和有用。
- 结构类型:它们现在允许可插拔实现,这极大地提高了它们的实用性。与现状相比,某些使用模式受到限制。
- 基于名称的模式匹配:现有的未记录的 Scala 2 实现已以略微简化的形式编入代码。
- Eta 展开 现在在没有预期类型的情况下也普遍执行。因此,后缀
_
运算符变得多余。它将在 Scala 3.0 之后被弃用并删除。 - 隐式解析:隐式解析规则已清理,以使它们更有用且不那么令人惊讶。隐式范围被限制为不再包含包前缀。
大多数旧式隐式解析的方面仍然可以在-source 3.0-migration
下使用。此列表中的其他更改将无条件应用。
状态:强烈建议
这些功能已在 Scala 3.0 的编译器中以其新形式实现。与现状相比,它们在简单性和功能方面提供了明显的改进。退回将需要大量的实施工作,而功能却会净损失。
迁移成本:低到高
只有少数程序需要更改,但一些必要的更改可能是非局部的(例如,对隐式范围的限制)。
新构造
这些是语言的补充,使语言更强大或更易于使用。
- 枚举为枚举和代数数据类型提供简洁的语法。
- 参数解元组避免了对元组参数解构使用
case
。 - 依赖函数类型将依赖方法推广到依赖函数值和类型。
- 多态函数类型将多态方法推广到依赖函数值和类型。当前状态:有一个提案和一个原型实现,但实现尚未最终确定或合并。
- 类型多态允许定义对类型和类型构造函数都起作用的操作符。
状态:混合
枚举提供了对基本使用模式的必要简化,因此应该将其用于 Scala 3.0。自动参数元组是一个非常小的更改,它消除了一些笨拙,因此现在可以采用它。其他功能构成更专门的功能,可以在以后的版本中引入。另一方面,除了多态函数类型之外,它们都已完全实现,因此如果 Scala 3.0 规范不包含它们,它们可能仍然可以在语言标志下提供。
迁移成本:无
作为新功能,现有代码无需更改即可迁移。可以肯定的是,有时重写代码以利用新功能以提高清晰度和简洁性会很有吸引力。
元编程
以下构建旨在为 Scala 中的元编程奠定新的基础。到目前为止,元编程是通过宏和库(例如 Shapeless)的组合实现的,而这些库又是基于一些关键宏的。当前的 Scala 2 宏机制是在当前 Scala 2 编译器之上的薄薄一层,这使得它们很脆弱,在许多情况下无法移植到 Scala 3。
值得注意的是,宏从未包含在 Scala 2 语言规范 中,到目前为止,它们仅在 -experimental
标志下可用。但这并没有阻止它们被广泛使用。
为了能够移植大多数宏的使用,我们正在尝试使用下面列出的高级语言结构。这些设计比 Scala 3.0 中提出的其他语言结构更具试探性。在最终发布之前,可能还会有一些变化。稳定元编程所需的特性集是我们的首要任务。
- 匹配类型 允许对类型进行计算。
- 内联 本身提供了一些简单宏的直接实现,同时也是实现复杂宏的必要构建块。
- 引用和拼接 提供了一种原则性的方法来表达宏和分阶段,并使用一组统一的抽象。
- 类型类推导 提供了 Shapeless 中
Gen
宏和其他基础库的语言内实现。新的实现比宏更健壮、更高效、更易于使用。 - 隐式按名称参数 提供了 Shapeless 中
Lazy
宏的更健壮的语言内实现。
状态:尚未确定
我们知道我们需要一个实用的替代方案来替代当前的宏。上面列出的特性在这方面非常有希望,但我们需要更完整的实现和更多用例才能得出最终结论。
迁移成本:非常高
现有的宏库将不得不从头开始重写。在许多情况下,重写的库将比旧的库更简单、更健壮,但这并不能减轻重写带来的成本。目前尚不清楚宏库的用户将受到多大程度的影响。我们的目标是提供足够的函数,以便可以完全重新实现核心宏,但鉴于 Scala 2 的各种宏扩展的庞大功能集,很难找到一个可行的范围限制。
类型检查和推断的变更
Scala 3 编译器使用一种新的类型推断算法,该算法依赖于一个通用的子类型约束求解器。新算法通常 比旧算法更有效,但不可避免地存在两种算法结果不同的情况,导致 Scala 3 对 Scala 2 编译器接受的程序诊断出错误。
状态:必不可少
新的类型检查和推断算法是新编译器的核心。如果不放弃整个 Scala 3 的实现,就无法恢复它们。
迁移成本:高
一些现有的程序将无法运行,并且鉴于类型推断的复杂性,并不总是清楚是什么变化导致了故障以及如何修复它。
根据我们的经验,宏和类型以及隐式参数推断的变更共同导致了将现有代码移植到 Scala 3 时遇到的绝大多数问题。后一种问题来源可以通过一个工具系统地解决,该工具将所有推断的类型和隐式参数添加到 Scala 2 源代码文件中。最有可能的是,这样的工具将作为 Scala 2 编译器插件 实现。生成的代码将极有可能在 Scala 3 下编译,但通常会过于庞大而无法阅读。重写工具的第二部分应该然后选择性地迭代地删除由第一部分合成的类型和隐式注释,只要它们在 Scala 3 下编译即可。第二部分可以实现为一个以编程方式调用 Scala 3 编译器 scalac
的程序。
一些人已经提出这样的工具有一段时间了。我相信现在是时候我们找到实现它的意愿和资源了。