Implicit Conversions - More Details
Implementation
An implicit conversion, or view, from type S
to type T
is defined by either:
- An
implicit def
which has typeS => T
or(=> S) => T
- An implicit value which has type
Conversion[S, T]
The standard library defines an abstract class Conversion
:
package scala
@java.lang.FunctionalInterface
abstract class Conversion[-T, +U] extends Function1[T, U]:
def apply(x: T): U
Function literals are automatically converted to Conversion
values.
Views are applied in three situations:
- If an expression
e
is of typeT
, andT
does not conform to the expression's expected typept
. In this case, an implicitv
which is applicable toe
and whose result type conforms topt
is searched. The search proceeds as in the case of implicit parameters, where the implicit scope is the one ofT => pt
. If such a view is found, the expressione
is converted tov(e)
. - In a selection
e.m
withe
of typeT
, if the selectorm
does not denote an accessible member ofT
. In this case, a viewv
which is applicable toe
and whose result contains an accessible member namedm
is searched. The search proceeds as in the case of implicit parameters, where the implicit scope is the one ofT
. If such a view is found, the selectione.m
is converted tov(e).m
. - In an application
e.m(args)
withe
of typeT
, if the selectorm
denotes some accessible member(s) ofT
, but none of these members is applicable to the argumentsargs
. In this case, a viewv
which is applicable toe
and whose result contains a methodm
which is applicable toargs
is searched. The search proceeds as in the case of implicit parameters, where the implicit scope is the one ofT => pt
, withpt
being the structural type{ def m(args: T_1 , ... , T_n): U }
. If such a view is found, the applicatione.m(args)
is converted tov(e).m(args)
.
Differences with Scala 2 implicit conversions
In Scala 2, views whose parameters are passed by-value take precedence over views whose parameters are passed by-name. This is no longer the case in Scala 3. A type error reporting the ambiguous conversions will be emitted in cases where this rule would be applied in Scala 2:
implicit def conv1(x: Int): String = x.toString
implicit def conv2(x: => Int): String = x.toString
val x: String = 0 // Compiles in Scala2 (uses `conv1`),
// type error in Scala 3 because of ambiguity.
In Scala 2, implicit values of a function type would be considered as potential views. In Scala 3, these implicit value need to have type Conversion
:
// Scala 2:
def foo(x: Int)(implicit conv: Int => String): String = x
// Becomes with Scala 3:
def foo(x: Int)(implicit conv: Conversion[Int, String]): String = x
// Call site is unchanged:
foo(4)(_.toString)
// Scala 2:
implicit val myConverter: Int => String = _.toString
// Becomes with Scala 3:
implicit val myConverter: Conversion[Int, String] = _.toString
Note that implicit conversions are also affected by the changes to implicit resolution between Scala 2 and Scala 3.
Motivation for the changes
The introduction of scala.Conversion
in Scala 3 and the decision to restrict implicit values of this type to be considered as potential views comes from the desire to remove surprising behavior from the language:
implicit val m: Map[Int, String] = Map(1 -> "abc")
val x: String = 1 // Scala 2: assigns "abc" to x
// Scala 3: type error
This snippet contains a type error. The right-hand side of val x
does not conform to type String
. In Scala 2, the compiler will use m
as an implicit conversion from Int
to String
, whereas Scala 3 will report a type error, because Map
isn't an instance of Conversion
.
Migration path
Implicit values that are used as views should see their type changed to Conversion
.
For the migration of implicit conversions that are affected by the changes to implicit resolution, refer to the Changes in Implicit Resolution for more information.
Reference
For more information about implicit resolution, see Changes in Implicit Resolution. Other details are available in PR #2065.