将来,Scala 3 将使用 _
下划线符号作为类型 lambda 中的占位符,就像当前在(普通)术语级 lambda 中使用下划线作为占位符一样。
新的类型 lambda 语法未默认启用,要启用它,请使用编译器标志 -Ykind-projector:underscores
。请注意,启用下划线类型 lambda 将禁用 _
作为通配符,您将只能使用 ?
符号编写通配符。
如果您希望同时为 Scala 2 和 Scala 3 交叉编译项目,同时为两者使用下划线类型 lambda,则可以使用 kind-projector 版本 0.13.0
及更高版本和 Scala 2 版本 2.13.6
和 2.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 不再需要参数上的 -
或 +
方差标记,这些标记现在是推断出来的。