Any plan to backport the diverging implicit resolving mechanism of Scala 3 to Scala 2?

I’m asking for improvements over the following 2 aspects:

  1. Error message, I’ll give an example to show the vast difference between the usability of 2 versions:

object DivergingImplicits {

  class ***[A, B]
  class >:<[A, B]
  class C
  trait D

  object Endo {
    implicit def f(implicit c: C): C = ???

    implicitly[C]
  }

  object Circular {
    implicit def f(implicit c: C): D = ???
    implicit def g(implicit d: D): C = ???

    implicitly[C]
  }

  object Diverging {
    trait ::[A, B]
    implicit def f[A, B](implicit ii: Int :: A :: B): A :: B = ???

    implicitly[C :: D]
  }
}

Error message of Scala 3:

-- Error: /home/peng/git/dottyspike/src/main/scala/com/tribbloids/spike/dotty/DivergingImplicits.scala:13:17 
13 |    implicitly[C]
   |                 ^
   |no implicit argument of type com.tribbloids.spike.dotty.DivergingImplicits.C was found for parameter e of method implicitly in object Predef.
   |I found:
   |
   |    com.tribbloids.spike.dotty.DivergingImplicits.Endo.f(
   |      /* missing */summon[com.tribbloids.spike.dotty.DivergingImplicits.C]
   |    )
   |
   |But method f in object Endo produces a diverging implicit search when trying to match type com.tribbloids.spike.dotty.DivergingImplicits.C.
   |
   |The following import might make progress towards fixing the problem:
   |
   |  import com.tribbloids.spike.dotty.DivergingImplicits.Circular.g
   |
-- Error: /home/peng/git/dottyspike/src/main/scala/com/tribbloids/spike/dotty/DivergingImplicits.scala:20:17 
20 |    implicitly[C]
   |                 ^
   |no implicit argument of type com.tribbloids.spike.dotty.DivergingImplicits.C was found for parameter e of method implicitly in object Predef.
   |I found:
   |
   |    com.tribbloids.spike.dotty.DivergingImplicits.Circular.g(
   |      com.tribbloids.spike.dotty.DivergingImplicits.Circular.f(
   |        /* missing */summon[com.tribbloids.spike.dotty.DivergingImplicits.C]
   |      )
   |    )
   |
   |But method g in object Circular produces a diverging implicit search when trying to match type com.tribbloids.spike.dotty.DivergingImplicits.C.
   |
   |The following import might make progress towards fixing the problem:
   |
   |  import com.tribbloids.spike.dotty.DivergingImplicits.Endo.f
   |
-- Error: /home/peng/git/dottyspike/src/main/scala/com/tribbloids/spike/dotty/DivergingImplicits.scala:27:22 
27 |    implicitly[C :: D]
   |                      ^
   |no implicit argument of type com.tribbloids.spike.dotty.DivergingImplicits.C :: 
   |  com.tribbloids.spike.dotty.DivergingImplicits.D was found for parameter e of method implicitly in object Predef.
   |I found:
   |
   |    com.tribbloids.spike.dotty.DivergingImplicits.Diverging.f[A, B](
   |      com.tribbloids.spike.dotty.DivergingImplicits.Diverging.f[A, B](
   |        /* missing */summon[Int :: A :: B]
   |      )
   |    )
   |
   |But method f in object Diverging produces a diverging implicit search when trying to match type Int :: A :: B.

Error message of Scala 2.13.6:

/home/peng/git/dottyspike/src/main/scala/com/tribbloids/spike/dottyDivergingImplicits.scala:16: diverging implicit expansion for type splain.DivergingImplicits.C
starting with method f in object Endo
/home/peng/git/dottyspike/src/main/scala/com/tribbloids/spike/dottyDivergingImplicits.scala:29: diverging implicit expansion for type splain.DivergingImplicits.C
starting with method f in object Circular
/home/peng/git/dottyspike/src/main/scala/com/tribbloids/spike/dottyDivergingImplicits.scala:39: diverging implicit expansion for type splain.DivergingImplicits.C :: splain.DivergingImplicits.D
starting with method f in object Diverging
  1. The handling of DivergingImplicitTypeError, To quote the documentation of scala 3:

The treatment of divergence errors has also changed. A divergent implicit is treated as a normal failure, after which alternatives are still tried. This also makes sense: Encountering a divergent implicit means that we assume that no finite solution can be found on the corresponding path, but another path can still be tried. By contrast, most (but not all) divergence errors in Scala 2 would terminate the implicit search as a whole.

(source: Changes in Implicit Resolution)

What does “most (but not all) divergence errors” entails? It was never clearly defined. The only thing I have discovered in the sourcecode is that when the termination happens, it will issue an DivergingImplicitTypeError directly to the context. This error is not even an ImplicitError and will bypass the “-Vimplicits” option as a whole. Thus, it cannot be improved by any compiler plugin.

As one of the definitive features of the language, I really feel that little effort has been spent to make the compiler more friendly to debugging and error reporting of this feature. Consequentially, employing it in production could be an infuriating experience and has been discouraged by many adopters.

Let me know your preference to improve it and I’ll try to summarise them and reach a consensus. I’m one of the maintainers of splain plugin, this could easily be the #1 improvement for its 1.0 release

1 Like