I believe that most of the need for implicit imports today comes from extension methods, like the toOK
method you describe:
implicit class EitherCanBeOk[L,R](private val underlying: Either[L,R]) {
def toOk: Ok[L, R] = underlying match {
case scala.util.Right(r) => Yes(r)
case scala.util.Left(l) => No(l)
}
}
In the future, if you want to make this method universally accessible, you’d define or export the extension method at the toplevel of your project. No implicit needed.
def (underlying: Either[L, R]) toOK[L, R] = underlying match {
case scala.util.Right(r) => Yes(r)
case scala.util.Left(l) => No(l)
}
So, let’s analyze when you would still need an implicit import by classifying the different kinds of implicits.
- Typeclasses: You need imports only for orphan instances, which should be avoided anyway if possible. If that’s not possible, a highly visible import is good since it alerts the reader that this code uses some orphan instances.
- Extension methods. Can be handled directly.
- Contexts such as
ExecutionContext
. If you have a root context, define it with the context class, as is done inExecutionContext.global
. If you want to override a third party root context with something in your project, this deserves a highly visible import. - Capabilities, configurations. Same remarks as for contexts apply.
- Implicit conversions. If they are defined with the source or target type, no import is necessary. If they are defined neither with the source type nor with the target they are evil. Requiring a highly visible import is the least we should do in that case (in fact we currently require a language import in addition to this).
Are there other classes of implicits? It would be good to add them to the classification.
In summary, so far, it seems that in future well-designed systems implicit imports should be both rare and would profit from being very visible. That’s what this proposal achieves.
The same cannot be said for today’s systems because of extension methods implemented by decorators. That’s why the proposal exempts current implicits from the import implied
tax. I.e. as long as you use implicit classes for extension methods, your users can get them with the same imports as before. But once a package switches to Scala 3 implicits, the import implied
is required.