Scala 2 中的 implicit
关键字和 Scala 3 中的 given
+using
有什么区别?它们只是将 implicit
拆分为两个关键字,还是语义上也有不同,如果有,具体是什么?
大部分时候,它们是一样的。然而,implicit
不再用于多个不同的概念。详细信息请查看文档,以下是它们的摘要:
当声明参数时,using
和 implicit
是一样的。但是,当显式传递隐式参数时,必须使用 using
:
def foo(using bar: Bar) = ???
foo(using Bar()) //Cannot use just foo(Bar()) as you would in Scala 2
给定实例与隐式值/对象/方法非常相似。
其中一个好处是它们可以是匿名的,编译器将为它们生成一个名称,如果给定的类型为F[X, Y]
,则名称看起来类似于given_F_X_Y
。更多细节请参阅此处。
另一个变化是,必须明确写出给定实例的类型 - 它不能像Scala 2中的隐式那样被推断。
没有参数的给定实例对应一个implicit object
。given 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 def
将 A
转换为 B
的隐式转换方式,在 Dotty 中可以定义一个给定的 Conversion[A, B]
实例。
在 Dotty 中仍然可以使用 implicit classes,但是你可以直接定义扩展方法。虽然扩展中的方法不能使用它们自己的类型参数,但比隐式类更易于使用。
Scala 3 新增了一项更改 - summon
是一个像implicitly
的方法,但它可以返回比请求更具体的类型。
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
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
http://dotty.epfl.ch/docs/reference/contextual/givens.html#negated-givens
http://dotty.epfl.ch/docs/reference/changed-features/implicit-resolution.html