Scala 2 隐式转换和 Scala 3 的 given/using 之间的区别

9

Scala 2 中的 implicit 关键字和 Scala 3 中的 given+using 有什么区别?它们只是将 implicit 拆分为两个关键字,还是语义上也有不同,如果有,具体是什么?

2个回答

6

大部分时候,它们是一样的。然而,implicit 不再用于多个不同的概念。详细信息请查看文档,以下是它们的摘要:

使用

当声明参数时,usingimplicit 是一样的。但是,当显式传递隐式参数时,必须使用 using

def foo(using bar: Bar) = ???
foo(using Bar()) //Cannot use just foo(Bar()) as you would in Scala 2

在Scala 3中,你也可以使用隐式的by-name参数。

给定实例

给定实例与隐式值/对象/方法非常相似。

其中一个好处是它们可以是匿名的,编译器将为它们生成一个名称,如果给定的类型为F[X, Y],则名称看起来类似于given_F_X_Y。更多细节请参阅此处

另一个变化是,必须明确写出给定实例的类型 - 它不能像Scala 2中的隐式那样被推断。

没有参数的给定实例对应一个implicit objectgiven foo: Foo with {...} 可以简化为 implicit object foo extends Foo {...}

带参数的给定实例类似于仅接受更多implicit参数的implicit def

given listOrd[T](using ord: Ord[T]): Ord[List[T]] with { ... }
//^^ this maps to this vv
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]

如果一个别名只是一个引用,那么它就成为了一个 implicit def;如果它是个懒惰的引用,则成为了一个 implicit lazy val
val foo: Foo
given Foo = foo

将变为final implicit def given_Foo = foo(请注意编译器生成的名称),但是。

given foo: Foo = new Foo()

将会变成 final implicit lazy val foo: Foo = new Foo(),因为 new Foo() 不应该被无端计算。


不再使用 implicit defA 转换为 B 的隐式转换方式,在 Dotty 中可以定义一个给定的 Conversion[A, B] 实例。

在 Dotty 中仍然可以使用 implicit classes,但是你可以直接定义扩展方法。虽然扩展中的方法不能使用它们自己的类型参数,但比隐式类更易于使用。

Scala 3 新增了一项更改 - summon 是一个像implicitly的方法,但它可以返回比请求更具体的类型。


2
语义也不同。在Scala 2中,可以使用歧义技巧定义Not
trait Not[A]
object Not {
  implicit def default[A]: Not[A] = null
  implicit def ambig[A](implicit a: A): Not[A] = null
}

implicitly[Not[Int]] // compiles

implicit val s: String = null
// implicitly[Not[String]] // doesn't compile

在Scala 3中,这种方法不起作用,因为歧义错误不会传播。
trait Not[A]
object Not {
  given [A]: Not[A] = null
  given [A](using a: A): Not[A] = null
  // given ambig[A](using a: A): Not[A] = null
}

summon[Not[Int]] // compiles

given String = null
summon[Not[String]] // compiles

应该使用 scala.util.NotGiven
summon[NotGiven[Int]] // compiles

given String = null
// summon[NotGiven[String]] // doesn't compile

(测试于3.0.0-M3-bin-20201211-dbc1186-NIGHTLY)

http://dotty.epfl.ch/docs/reference/contextual/givens.html#negated-givens

http://dotty.epfl.ch/docs/reference/changed-features/implicit-resolution.html


1
不错,我没有想到这个!我认为在Scala 3的隐式解析变化页面上也有一个例子,说明模棱两可的错误会阻止搜索成功。 - user
Scala 3: 反对 Givens,由 Rock the JVM 主讲。 - Dmytro Mitin
https://blog.rockthejvm.com/anti-givens/ - Dmytro Mitin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接