Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between Scala 2 implicits and Scala 3 given/using

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?

like image 354
user Avatar asked Dec 12 '20 22:12

user


1 Answers

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.

like image 60
3 revs Avatar answered Sep 30 '22 16:09

3 revs