Implicit Conversions - More Details
Implementation
An implicit conversion, or view, from type S to type T is defined by either:
- An implicit defwhich has typeS => Tor(=> 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 eis of typeT, andTdoes not conform to the expression's expected typept. In this case, an implicitvwhich is applicable toeand whose result type conforms toptis 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 expressioneis converted tov(e).
- In a selection e.mwitheof typeT, if the selectormdoes not denote an accessible member ofT. In this case, a viewvwhich is applicable toeand whose result contains an accessible member namedmis 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.mis converted tov(e).m.
- In an application e.m(args)witheof typeT, if the selectormdenotes some accessible member(s) ofT, but none of these members is applicable to the argumentsargs. In this case, a viewvwhich is applicable toeand whose result contains a methodmwhich is applicable toargsis 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 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.