Scala 3 迁移指南

上下文抽象

语言

上下文抽象 的重新设计带来了一些不兼容性。

不兼容性 Scala 2.13 Scala 3 迁移重写 Scalafix 规则 运行时不兼容性
隐式 def 的类型      
隐式视图       可能
视图边界 弃用      
A=> A 上的转换不明确        

隐式定义的类型

隐式定义的类型(valdef)需要在 Scala 3 中显式给出。它们不能再被推断了。

名为 ExplicitResultTypes 的 Scalafix 规则可以自动编写缺少的类型注释。

隐式视图

Scala 3 不支持从隐式函数值(形式为 implicit val ev: A => B)进行隐式转换。

以下代码段在 Scala 3 中现在无效

trait Pretty {
  val print: String
}

def pretty[A](a: A)(implicit ev: A => Pretty): String =
  a.print // In Scala 3, Error: value print is not a member of A

Scala 3 迁移编译 可以针对这些情况发出警告,但不会尝试修复它。

请注意,此不兼容性可能会产生运行时不兼容性并破坏您的程序。实际上,编译器可以从更广泛的作用域中找到另一个隐式转换,这最终会导致在运行时出现不需要的行为。

此示例说明了这种情况

trait Pretty {
  val print: String
}

implicit def anyPretty(any: Any): Pretty = new Pretty { val print = "any" }

def pretty[A](a: A)(implicit ev: A => Pretty): String =
  a.print // always print "any"

已解析的转换取决于编译器模式

  • -source:3.0-migration:编译器执行 ev 转换
  • -source:3.0:编译器无法执行 ev 转换,但可以执行 anyPretty,这是不需要的

在 Scala 3 中,一个简单的修复方法是明确提供正确的转换

def pretty[A](a: A)(implicit ev: A => Pretty): String =
-  a.print
+  ev(a).print

视图界限

视图界限已弃用很长时间,但仍在 Scala 2.13 中受支持。它们不再可以与 Scala 3 一起编译。

def foo[A <% Long](a: A): Long = a

在此示例中,在 Scala 3 中,我们收到以下错误消息

-- Error: src/main/scala/view-bound.scala:2:12 
2 |  def foo[A <% Long](a: A): Long = a
  |            ^
  |          view bounds `<%' are deprecated, use a context bound `:' instead

该消息建议使用上下文界限而不是视图界限,但这会更改方法的签名。保留二进制兼容性可能更容易且更安全。要做到这一点,必须显式声明并调用隐式转换。

小心不要陷入上面在 隐式视图 中描述的运行时不兼容性。

-def foo[A <% Long](a: A): Long = a
+def foo[A](a: A)(implicit ev: A => Long): Long = ev(a)

A=> A 上的模棱两可的转换

在 Scala 2.13 中,在 A 上的隐式转换优于在 => A 上的隐式转换。在 Scala 3 中不再是这样,并导致模棱两可的转换。

例如,在此示例中

implicit def boolFoo(bool: Boolean): Foo = ???
implicit def lazyBoolFoo(lazyBool:  => Boolean): Foo = ???

true.foo()

Scala 2.13 编译器选择 boolFoo 转换,但 Scala 3 编译器无法编译。

-- Error: src/main/scala/ambiguous-conversion.scala:4:19
9 |  true.foo()
  |  ^^^^
  |Found:    (true : Boolean)
  |Required: ?{ foo: ? }
  |Note that implicit extension methods cannot be applied because they are ambiguous;
  |both method boolFoo in object Foo and method lazyBoolFoo in object Foo provide an extension method `foo` on (true : Boolean)

一个临时解决方案是显式编写转换。

implicit def boolFoo(bool: Boolean): Foo = ???
implicit def lazyBoolFoo(lazyBool:  => Boolean): Foo = ???

-true.foo()
+boolFoo(true).foo()

此页面的贡献者