与 Scala 2 隐式的关系
Scala 3 中的许多(但并非全部)新的上下文抽象功能都可以映射到 Scala 2 的隐式。此页面概述了新旧功能之间的关系。
使用 Scala 2 隐式模拟 Scala 3 上下文抽象概念
给定实例
给定实例可以映射到隐式对象、类和隐式方法的组合。
-
没有参数的给定实例映射到隐式对象。例如,
given intOrd: Ord[Int] with { ... }
映射到
implicit object intOrd extends Ord[Int] { ... }
-
带参数的给定实例映射到类和隐式方法的组合。例如,
given listOrd[T](using ord: Ord[T]): Ord[List[T]] with { ... }
映射到
class listOrd[T](implicit ord: Ord[T]) extends Ord[List[T]] { ... } final implicit def listOrd[T](implicit ord: Ord[T]): listOrd[T] = new listOrd[T]
-
别名给定实例映射到隐式方法或隐式惰性值。如果别名既没有类型也没有上下文参数,则将其视为惰性值,除非右侧是一个简单引用,在这种情况下,我们可以使用转发器指向该引用而不缓存它。
示例
given global: ExecutionContext = new ForkJoinContext()
val ctx: Context
given Context = ctx
将映射到
final implicit lazy val global: ExecutionContext = new ForkJoinContext()
final implicit def given_Context = ctx
匿名给定实例
匿名给定实例获取编译器合成的名称,这些名称以可重现的方式从实现的类型中生成。例如,如果上面给出的 IntOrd
和 ListOrd
的名称被省略,则会合成以下名称
given given_Ord_Int: Ord[Int] with { ... }
given given_Ord_List[T](using ord: Ord[T]): Ord[List[T]] with { ... }
合成的类型名称由以下部分组成
- 前缀
given_
, - 实现的类型的简单名称(省略任何前缀),
- 这些类型的顶级参数类型构造函数的简单名称。
元组被视为透明的,即类型 F[(X, Y)]
将获得合成的名称 F_X_Y
。直接实现的函数类型 A => B
表示为 A_to_B
。用作其他类型构造函数参数的函数类型表示为 Function
。
使用子句
使用子句在很大程度上对应于 Scala 2 的隐式参数子句。例如,
def max[T](x: T, y: T)(using ord: Ord[T]): T
将被编写为
def max[T](x: T, y: T)(implicit ord: Ord[T]): T
在 Scala 2 中。主要区别在于此类参数的应用。使用子句的参数的显式参数必须使用 (using ...)
编写,以反映定义语法。例如,max(2, 3)(using IntOrd)
。Scala 2 使用普通应用 max(2, 3)(IntOrd)
。Scala 2 语法有一些固有的歧义和限制,新语法克服了这些歧义和限制。例如,旧语法中没有多个隐式参数列表,即使可以使用“Aux”模式中的辅助对象模拟它们也是如此。
summon
方法对应于 Scala 2 中的 implicitly
。它与 Shapeless 中的 the
方法完全相同。summon
(或 the
)与 implicitly
之间的区别在于 summon
可以返回比所请求类型更精确的类型。
上下文界限
上下文界限在两种语言版本中都是相同的。它们扩展到隐式参数的各个形式。
注意:为了简化迁移,Scala 3 中的上下文界限在有限时间内映射到旧式隐式参数,这些参数可以在 using 子句或普通参数列表中传递。一旦旧式隐式参数被弃用,上下文界限将映射到 using 子句。
扩展方法
扩展方法在 Scala 2 中没有直接对应项,但可以使用隐式类来模拟它们。例如,扩展方法
extension (c: Circle)
def circumference: Double = c.radius * math.Pi * 2
可以通过以下方式在一定程度上模拟
implicit class CircleDecorator(c: Circle) extends AnyVal {
def circumference: Double = c.radius * math.Pi * 2
}
在给定实例中实现的特质中的抽象扩展方法在 Scala 2 中没有直接对应项。模拟这些方法的唯一方法是通过导入使隐式类可用。Simulacrum 宏库可以在某些情况下自动化此过程。
类型类派生
类型类派生在 Scala 2 语言中没有直接对应项。可以通过基于宏的库(例如 Shapeless、Magnolia 或 scalaz-deriving)实现类似的功能。
上下文函数类型
上下文函数类型在 Scala 2 中没有类似项。
隐式按名称参数
Scala 2 中不支持隐式按名称参数,但可以在一定程度上通过 Shapeless 中的 Lazy
类型进行模拟。
在 Scala 3 中模拟 Scala 2 隐式
隐式转换
Scala 2 中的隐式转换方法可以在 Scala 3 中表示为 scala.Conversion
类的给定实例。例如,可以编写
implicit def stringToToken(str: String): Token = new Keyword(str)
而不是
given stringToToken: Conversion[String, Token] with
def apply(str: String): Token = KeyWord(str)
或
given stringToToken: Conversion[String, Token] = KeyWord(_)
隐式类
Scala 2 中的隐式类通常用于定义扩展方法,Scala 3 中直接支持扩展方法。隐式类的其他用途可以通过一对常规类和给定的 Conversion
实例来模拟。
隐式值
Scala 2 中的隐式 val
定义可以在 Scala 3 中使用常规 val
定义和别名 given 来表示。例如,Scala 2 的
lazy implicit val pos: Position = tree.sourcePos
可以用 Scala 3 表示为
lazy val pos: Position = tree.sourcePos
given Position = pos
抽象隐式
Scala 2 中的抽象隐式 val
或 def
可以使用常规抽象定义和给定的别名在 Scala 3 中表示。例如,Scala 2 的
implicit def symDecorator: SymDecorator
可以用 Scala 3 表示为
def symDecorator: SymDecorator
given SymDecorator = symDecorator
实现状态和时间表
Scala 3 实现同时实现了 Scala 2 的隐式和新抽象。事实上,对 Scala 2 隐式的支持是 2.13 和 Scala 3 之间通用语言子集的重要组成部分。通过提供自动重写功能,将支持向新抽象的迁移。
根据采用模式,旧式隐式可能会在 Scala 3.0 之后的版本中开始弃用。