Difference between Scala 2 implicits and Scala 3 given/using
Asked Answered
B

2

9

What is the difference between the implicit keyword in Scala 2 and given+using in Scala 3? Is it just that implicit has been split up into two keywords, or are the semantics also different, and if so, how?

Bullfighter answered 12/12, 2020 at 22:18 Comment(0)
T
6

For the most part, they are the same. However, implicit is no longer used for multiple different concepts. The docs go into more detail, but here's a summary of them:

Using

When declaring parameters, using is just the same as implicit. However, when explicitly passing an implicit argument, you must use using:

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

You can also have implicit by-name parameters in Scala 3.


Given

Givens are also pretty similar to implicit vals/objects/methods.

One nice thing about them is that they can be anonymous, and the compiler will generate a name for them, which looks something like given_F_X_Y if the type of the given were F[X, Y]. More details here.

Another change is that the type of a given must be written explicitly - it cannot be inferred like for an implicit in Scala 2.

A given without parameters maps to an implicit object. given foo: Foo with {...} becomes just implicit object foo extends Foo {...}.

A given with parameters is akin to an implicit def that takes in only more implicit parameters.

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]

A given that is merely an alias becomes an implicit def if it is just a reference, or an implicit lazy val otherwise.

val foo: Foo
given Foo = foo

would become final implicit def given_Foo = foo (note the compiler-generated name), but

given foo: Foo = new Foo()

would turn into final implicit lazy val foo: Foo = new Foo() because new Foo() shouldn't be computed unnecessarily.


Instead of using an implicit def for an implicit conversion from A to B, you can now define a given Conversion[A, B] instance.

You can also still use implicit classes in Dotty, but you can directly define extension methods. While methods inside extensions cannot take their own type parameters, they are easier to use than implicit classes.

An additional change in Scala 3 - summon is a method like implicitly, but it can return a type more specific than the one being requested.

Truthful answered 12/12, 2020 at 22:18 Comment(0)
P
2

Semantics is also different. In Scala 2 Not can be defined with ambiguity trick

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

But in Scala 3 this doesn't work because ambiguity error is not propagated

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

One should use scala.util.NotGiven instead

summon[NotGiven[Int]] // compiles

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

(Tested in 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

Palatable answered 13/12, 2020 at 4:34 Comment(3)
Nice, I didn’t think of this! I think there is also an example on the Changes in Implicit Resolution page somewhere about how an ambiguity error doesn’t let a search succeed in Scala 3Bullfighter
youtube.com/watch?v=HQojyuZK-Uo Scala 3: Anti-Givens by Rock the JVMPalatable
blog.rockthejvm.com/anti-givensPalatable

© 2022 - 2024 — McMap. All rights reserved.