Scala 3 迁移指南

Kind Projector 迁移

语言

将来,Scala 3 将使用 _ 下划线符号作为类型 lambda 中的占位符,就像当前在(普通)术语级 lambda 中使用下划线作为占位符一样。

新的类型 lambda 语法未默认启用,要启用它,请使用编译器标志 -Ykind-projector:underscores。请注意,启用下划线类型 lambda 将禁用 _ 作为通配符,您将只能使用 ? 符号编写通配符。

如果您希望同时为 Scala 2 和 Scala 3 交叉编译项目,同时为两者使用下划线类型 lambda,则可以使用 kind-projector 版本 0.13.0 及更高版本和 Scala 2 版本 2.13.62.12.14 来实现。要启用它,请将编译器标志 -Xsource:3 -P:kind-projector:underscore-placeholders 添加到您的构建中。与 Scala 3 中一样,这将禁用 _ 作为通配符,但是,标志 -Xsource:3 将允许您用 ? 符号替换它。

以下 sbt 配置将设置正确的标志以使用新语法进行交叉编译

ThisBuild / scalacOptions ++= {
  CrossVersion.partialVersion(scalaVersion.value) match {
    case Some((3, _)) => Seq("-Ykind-projector:underscores")
    case Some((2, 12 | 13)) => Seq("-Xsource:3", "-P:kind-projector:underscore-placeholders")
  }
}

迁移到新语法

要在已启用 kind-projector 的现有代码中使用下划线作为类型 lambda,请将 *? 类型 lambda 占位符替换为 _

反过来,你还必须将 _ 作为通配符的所有用法重写为使用 ? 符号。

例如,通配符的以下用法

def getWidget(widgets: Set[_ <: Widget], name: String): Option[Widget] = widgets.find(_.name == name) 

必须重写为

def getWidget(widgets: Set[? <: Widget], name: String): Option[Widget] = widgets.find(_.name == name) 

以及 kind-projector 的 * 占位符的以下用法

Tuple2[*, Double]        // equivalent to: type R[A] = Tuple2[A, Double]
Either[Int, +*]          // equivalent to: type R[+A] = Either[Int, A]
Function2[-*, Long, +*]  // equivalent to: type R[-A, +B] = Function2[A, Long, B]

必须重写为

Tuple2[_, Double]        // equivalent to: type R[A] = Tuple2[A, Double]
Either[Int, +_]          // equivalent to: type R[+A] = Either[Int, A]
Function2[-_, Long, +_]  // equivalent to: type R[-A, +B] = Function2[A, Long, B]

编译现有代码

即使不迁移到下划线类型 lambda,你可能仍然能够在不进行更改的情况下使用 Scala 3 编译大部分代码。

使用标志 -Ykind-projector 启用对基于 * 的类型 lambda 的支持(不启用下划线类型 lambda),以下形式现在将编译

Tuple2[*, Double]        // equivalent to: type R[A] = Tuple2[A, Double]
Either[Int, +*]          // equivalent to: type R[+A] = Either[Int, A]
Function2[-*, Long, +*]  // equivalent to: type R[-A, +B] = Function2[A, Long, B]

重写不兼容的构造

Scala 3 的 -Ykind-projector-Ykind-projector:underscores 仅实现 kind-projector 语法的子集,特别是它们不实现

  • 高阶类型 lambda 占位符
  • 高阶命名类型 lambda 参数
  • Lambda 关键字(λ 仍然受支持)

你必须重写以下所有形式

// classic
EitherT[*[_], Int, *]    // equivalent to: type R[F[_], B] = EitherT[F, Int, B]
// underscores
EitherT[_[_], Int, _]    // equivalent to: type R[F[_], B] = EitherT[F, Int, B]
// named λ
λ[(F[_], A) => EitherT[F, Int, A]]
// named Lambda
Lambda[(F[_], A) => EitherT[F, Int, A]]

进入以下长格式以使用 Scala 3 进行交叉编译

type MyLambda[F[_], A] = EitherT[F, Int, A]
MyLambda

或者,如果你不需要进行交叉编译,则可以使用 Scala 3 的 原生类型 Lambda

[F[_], A] =>> EitherT[F, Int, A]

对于 Lambda,你必须重写以下形式

Lambda[(`+E`, `+A`) => Either[E, A]]

为以下形式以进行交叉编译

λ[(`+E`, `+A`) => Either[E, A]]

或者,也可以使用 Scala 3 类型 lambda

[E, A] =>> Either[E, A]

注意:Scala 3 类型 lambda 不再需要参数上的 -+ 方差标记,这些标记现在是推断出来的。

此页面的贡献者