From e30781de1c61f677f189b6dcca60f668bfff9f93 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 31 Oct 2018 12:03:22 +0100 Subject: [PATCH 1/4] More details on implicit conversions --- .../reference/changed/implicit-conversions.md | 124 +++++++++++++----- 1 file changed, 94 insertions(+), 30 deletions(-) diff --git a/docs/docs/reference/changed/implicit-conversions.md b/docs/docs/reference/changed/implicit-conversions.md index 96299223b3f2..bb5e889db4f9 100644 --- a/docs/docs/reference/changed/implicit-conversions.md +++ b/docs/docs/reference/changed/implicit-conversions.md @@ -1,49 +1,113 @@ --- layout: doc-page -title: "Restrictions to Implicit Conversions" +title: "Implicit Conversions - More Details" --- -Previously, an implicit value of type `Function1`, or any of its subtypes -could be used as an implicit conversion. That is, the following code would compile -even though it probably masks a type error: +## Implementation - implicit val m: Map[Int, String] = Map(1 -> "abc") +An implicit conversion, or _view_, from type `S` to type `T` is +defined by either: - val x: String = 1 // scalac: assigns "abc" to x - // Dotty: type error +- An `implicit def` which has type `S => T` or `(=> S) => T` +- An implicit value which has type `ImplicitConverter[S, T]` -By contrast, Dotty only considers _methods_ as implicit conversions, so the -`Map` value `m` above would not qualify as a conversion from `String` to `Int`. +The standard library defines an abstract class `ImplicitConverter`: -To be able to express implicit conversions passed as parameters, `Dotty` -introduces a new type +```scala +abstract class ImplicitConverter[-T, +U] extends Function1[T, U] +``` - abstract class ImplicitConverter[-T, +U] extends Function1[T, U] +Function literals are automatically converted to `ImplicitConverter` +values. -Implicit values of type `ImplicitConverter[A, B]` do qualify as implicit -conversions. It is as if there was a global implicit conversion method +Views are applied in three situations: - def convert[A, B](x: A)(implicit converter: ImplicitConverter[A, B]): B = - converter(x) +1. If an expression `e` is of type `T`, and `T` does not conform to + the expression's expected type `pt`. In this case, an implicit `v` + which is applicable to `e` and whose result type conforms to `pt` + is searched. The search proceeds as in the case of implicit + parameters, where the implicit scope is the one of `T => pt`. If + such a view is found, the expression `e` is converted to `v(e)`. +1. In a selection `e.m` with `e` of type `T`, if the selector `m` does + not denote an accessible member of `T`. In this case, a view `v` + which is applicable to `e` and whose result contains an accessible + member named `m` is searched. The search proceeds as in the case of + implicit parameters, where the implicit scope is the one of `T`. If + such a view is found, the selection `e.m` is converted to `v(e).m`. +1. In an application `e.m(args)` with `e` of type `T`, if the selector + `m` denotes some accessible member(s) of `T`, but none of these + members is applicable to the arguments `args`. In this case, a view + `v` which is applicable to `e` and whose result contains a method + `m` which is applicable to `args` is searched. The search proceeds + as in the case of implicit parameters, where the implicit scope is + the one of `T`. If such a view is found, the application + `e.m(args)` is converted to `v(e).m(args)`. -(In reality the Dotty compiler simulates the behavior of this method directly in -its type checking because this turns out to be more efficient). +# Differences with Scala 2 implicit conversions -In summary, previous code using implicit conversion parameters such as +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. - def useConversion(implicit f: A => B) = { - val y: A = ... - val x: B = y // error under Dotty - } +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 +`ImplicitConverter`: -is no longer legal and has to be rewritten to +```scala +// Scala 2: +def foo(x: Int)(implicit conv: Int => String): String = x - def useConversion(implicit f: ImplicitConverter[A, B]) = { - val y: A = ... - val x: B = y // OK - } +// Becomes with Scala 3: +def foo(x: Int)(implicit conv: ImplicitConverter[Int, String]): String = x -### Reference +// Call site is unchanged: +foo(4)(_.toString) -For more info, see [PR #2065](https://github.com/lampepfl/dotty/pull/2065). +// Scala 2: +implicit val myConverter: Int => String = _.toString + +// Becomes with Scala 3: +implicit val myConverter: ImplicitConverter[Int, String] = _.toString +``` + +Note that implicit conversions are also affected by the [changes to +implicit resolution](implicit-resolution.html) between Scala 2 and +Scala 3. + +## Motivation for the changes + +The introduction of `ImplicitConverter` 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: + +```scala +implicit val m: Map[Int, String] = Map(1 -> "abc") + +val x: String = 1 // scalac: assigns "abc" to x + // Dotty: 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 +`ImplicitConverter`. + +## Migration path + +Implicit values that are used as views should see their type changed +to `ImplicitConverter`. + +For the migration of implicit conversions that are affected by the +changes to implicit resolution, refer to the [Changes in Implicit +Resolution](implicit-resolution.html) for more information. + +## Reference + +For more information about implicit resolution, see [Changes in +Implicit Resolution](implicit-resolution.html). +Other details are available in +[PR #2065](https://github.com/lampepfl/dotty/pull/2065) From a9464d37d88c8c947271b59fb91e6ae52d7caf25 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 31 Oct 2018 16:24:03 +0100 Subject: [PATCH 2/4] Split doc about `Implicit Conversions` --- .../changed/implicit-conversions-spec.md | 113 +++++++++++++++ .../reference/changed/implicit-conversions.md | 136 ++++++------------ 2 files changed, 154 insertions(+), 95 deletions(-) create mode 100644 docs/docs/reference/changed/implicit-conversions-spec.md diff --git a/docs/docs/reference/changed/implicit-conversions-spec.md b/docs/docs/reference/changed/implicit-conversions-spec.md new file mode 100644 index 000000000000..bb5e889db4f9 --- /dev/null +++ b/docs/docs/reference/changed/implicit-conversions-spec.md @@ -0,0 +1,113 @@ +--- +layout: doc-page +title: "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 type `S => T` or `(=> S) => T` +- An implicit value which has type `ImplicitConverter[S, T]` + +The standard library defines an abstract class `ImplicitConverter`: + +```scala +abstract class ImplicitConverter[-T, +U] extends Function1[T, U] +``` + +Function literals are automatically converted to `ImplicitConverter` +values. + +Views are applied in three situations: + +1. If an expression `e` is of type `T`, and `T` does not conform to + the expression's expected type `pt`. In this case, an implicit `v` + which is applicable to `e` and whose result type conforms to `pt` + is searched. The search proceeds as in the case of implicit + parameters, where the implicit scope is the one of `T => pt`. If + such a view is found, the expression `e` is converted to `v(e)`. +1. In a selection `e.m` with `e` of type `T`, if the selector `m` does + not denote an accessible member of `T`. In this case, a view `v` + which is applicable to `e` and whose result contains an accessible + member named `m` is searched. The search proceeds as in the case of + implicit parameters, where the implicit scope is the one of `T`. If + such a view is found, the selection `e.m` is converted to `v(e).m`. +1. In an application `e.m(args)` with `e` of type `T`, if the selector + `m` denotes some accessible member(s) of `T`, but none of these + members is applicable to the arguments `args`. In this case, a view + `v` which is applicable to `e` and whose result contains a method + `m` which is applicable to `args` is searched. The search proceeds + as in the case of implicit parameters, where the implicit scope is + the one of `T`. If such a view is found, the application + `e.m(args)` is converted to `v(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. + +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 +`ImplicitConverter`: + +```scala +// Scala 2: +def foo(x: Int)(implicit conv: Int => String): String = x + +// Becomes with Scala 3: +def foo(x: Int)(implicit conv: ImplicitConverter[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: ImplicitConverter[Int, String] = _.toString +``` + +Note that implicit conversions are also affected by the [changes to +implicit resolution](implicit-resolution.html) between Scala 2 and +Scala 3. + +## Motivation for the changes + +The introduction of `ImplicitConverter` 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: + +```scala +implicit val m: Map[Int, String] = Map(1 -> "abc") + +val x: String = 1 // scalac: assigns "abc" to x + // Dotty: 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 +`ImplicitConverter`. + +## Migration path + +Implicit values that are used as views should see their type changed +to `ImplicitConverter`. + +For the migration of implicit conversions that are affected by the +changes to implicit resolution, refer to the [Changes in Implicit +Resolution](implicit-resolution.html) for more information. + +## Reference + +For more information about implicit resolution, see [Changes in +Implicit Resolution](implicit-resolution.html). +Other details are available in +[PR #2065](https://github.com/lampepfl/dotty/pull/2065) + diff --git a/docs/docs/reference/changed/implicit-conversions.md b/docs/docs/reference/changed/implicit-conversions.md index bb5e889db4f9..5d7728d77ca0 100644 --- a/docs/docs/reference/changed/implicit-conversions.md +++ b/docs/docs/reference/changed/implicit-conversions.md @@ -1,113 +1,59 @@ --- layout: doc-page -title: "Implicit Conversions - More Details" +title: "Implicit Conversions" --- -## Implementation +An _implicit conversion_, also called _view_, is a conversion that +is applied by the compiler in several situations: -An implicit conversion, or _view_, from type `S` to type `T` is -defined by either: +1. When an expression `e` of type `T` is encountered, but the compiler + needs an expression of type `S`. +1. When an expression `e.m` where `e` has type `T` but `T` defines no + member `m` is encountered. -- An `implicit def` which has type `S => T` or `(=> S) => T` -- An implicit value which has type `ImplicitConverter[S, T]` +In those cases, the compiler looks in the implicit scope for a +conversion that can convert an expression of type `T` to an expression +of type `S` (or to a type that defines a member `m` in the second +case). -The standard library defines an abstract class `ImplicitConverter`: +This conversion can be either: -```scala -abstract class ImplicitConverter[-T, +U] extends Function1[T, U] -``` - -Function literals are automatically converted to `ImplicitConverter` -values. - -Views are applied in three situations: - -1. If an expression `e` is of type `T`, and `T` does not conform to - the expression's expected type `pt`. In this case, an implicit `v` - which is applicable to `e` and whose result type conforms to `pt` - is searched. The search proceeds as in the case of implicit - parameters, where the implicit scope is the one of `T => pt`. If - such a view is found, the expression `e` is converted to `v(e)`. -1. In a selection `e.m` with `e` of type `T`, if the selector `m` does - not denote an accessible member of `T`. In this case, a view `v` - which is applicable to `e` and whose result contains an accessible - member named `m` is searched. The search proceeds as in the case of - implicit parameters, where the implicit scope is the one of `T`. If - such a view is found, the selection `e.m` is converted to `v(e).m`. -1. In an application `e.m(args)` with `e` of type `T`, if the selector - `m` denotes some accessible member(s) of `T`, but none of these - members is applicable to the arguments `args`. In this case, a view - `v` which is applicable to `e` and whose result contains a method - `m` which is applicable to `args` is searched. The search proceeds - as in the case of implicit parameters, where the implicit scope is - the one of `T`. If such a view is found, the application - `e.m(args)` is converted to `v(e).m(args)`. +1. An `implicit def` of type `T => S` or `(=> T) => S` +1. An implicit value of type `ImplicitConverter[T, S]` -# Differences with Scala 2 implicit conversions +## Examples -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. - -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 -`ImplicitConverter`: +The first example is taken from `scala.Predef`. Thanks to this +implicit conversion, it is possible to pass a `scala.Int` to a Java +method that expects a `java.lang.Integer` ```scala -// Scala 2: -def foo(x: Int)(implicit conv: Int => String): String = x - -// Becomes with Scala 3: -def foo(x: Int)(implicit conv: ImplicitConverter[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: ImplicitConverter[Int, String] = _.toString +import scala.language.implicitConversions +implicit def int2Integer(x: Int): java.lang.Integer = + x.asInstanceOf[java.lang.Integer] ``` -Note that implicit conversions are also affected by the [changes to -implicit resolution](implicit-resolution.html) between Scala 2 and -Scala 3. - -## Motivation for the changes - -The introduction of `ImplicitConverter` 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: +The second example shows how to use `ImplicitConverter` to define an +`Ordering` for an arbitrary type, given existing `Ordering`s for other +types: ```scala -implicit val m: Map[Int, String] = Map(1 -> "abc") - -val x: String = 1 // scalac: assigns "abc" to x - // Dotty: type error +implicit def ordT[T, S]( + implicit conv: ImplicitConverter[T, S], + ordS: Ordering[S] + ): Ordering[T] = { + // `ordS` compares values of type `S`, but we can convert from `T` to `S` + (x: T, y: T) => ordS.compare(x, y) +} + +class A(val x: Int) // The type for which we want an `Ordering` + +// Convert `A` to a type for which an `Ordering` is available: +implicit val AToInt: ImplicitConverter[A, Int] = _.x + +implicitly[Ordering[Int]] // Ok, exists in the standard library +implicitly[Ordering[A]] // Ok, will use the implicit conversion from + // `A` to `Int` and the `Ordering` for `Int`. ``` -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 -`ImplicitConverter`. - -## Migration path - -Implicit values that are used as views should see their type changed -to `ImplicitConverter`. - -For the migration of implicit conversions that are affected by the -changes to implicit resolution, refer to the [Changes in Implicit -Resolution](implicit-resolution.html) for more information. - -## Reference - -For more information about implicit resolution, see [Changes in -Implicit Resolution](implicit-resolution.html). -Other details are available in -[PR #2065](https://github.com/lampepfl/dotty/pull/2065) - +[More details](implicit-conversions-spec.html) From 3ab5bcc24eb5e5c51c2c051a00b8593aa0afd271 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 31 Oct 2018 16:29:05 +0100 Subject: [PATCH 3/4] Add example that no longer compiles with Scala 3 --- .../reference/changed/implicit-conversions-spec.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/docs/reference/changed/implicit-conversions-spec.md b/docs/docs/reference/changed/implicit-conversions-spec.md index bb5e889db4f9..d1bf918421bb 100644 --- a/docs/docs/reference/changed/implicit-conversions-spec.md +++ b/docs/docs/reference/changed/implicit-conversions-spec.md @@ -48,7 +48,15 @@ Views are applied in three situations: 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. +be emitted in cases where this rule would be applied in Scala 2: + +```scala +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 From 844ad0e7b5d84fc6df1674f2c6cfec7229b498d9 Mon Sep 17 00:00:00 2001 From: Martin Duhem Date: Wed, 31 Oct 2018 16:41:14 +0100 Subject: [PATCH 4/4] Mention `scala.language.implicitConversions` --- docs/docs/reference/changed/implicit-conversions.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/docs/reference/changed/implicit-conversions.md b/docs/docs/reference/changed/implicit-conversions.md index 5d7728d77ca0..67bccc4933d6 100644 --- a/docs/docs/reference/changed/implicit-conversions.md +++ b/docs/docs/reference/changed/implicit-conversions.md @@ -21,6 +21,10 @@ This conversion can be either: 1. An `implicit def` of type `T => S` or `(=> T) => S` 1. An implicit value of type `ImplicitConverter[T, S]` +Defining an implicit conversion will emit a warning unless the import +`scala.language.implicitConversions` is in scope, or the flag +`-language:implicitConversions` is given to the compiler. + ## Examples The first example is taken from `scala.Predef`. Thanks to this @@ -38,6 +42,7 @@ The second example shows how to use `ImplicitConverter` to define an types: ```scala +import scala.language.implicitConversions implicit def ordT[T, S]( implicit conv: ImplicitConverter[T, S], ordS: Ordering[S]