We want in the future to remove implicit classes in favor of extension methods, but they have some unique behaviors that extension methods fail to imitate currently. One of these is the shadowing mechanism that affects extension methods like regular methods, but does not affect implicit classes because their behavior is derived from implicit conversions.
Example:
import scala.annotation.targetName
trait Foo[+T]
object Ops:
extension (foo : Foo[Int])
@targetName("bazInt")
def baz : Unit = {}
import Ops.*
extension (foo : Foo[Double])
@targetName("bazDouble")
def baz : Unit = {}
val f = new Foo[Int] {}
f.baz //error
The error is:
value baz is not a member of Playground.Foo[Int].
An extension method was tried, but could not be fully constructed:
Playground.baz(f) failed with
Found: (Playground.f : Playground.Foo[Int])
Required: Playground.Foo[Double]
In Scala 3 the name shadowing mechanism was disabled for implicits. I think it should be the same for extension methods, not only to match the implicit class behavior, but also because I believe itâs the more intuitive approach. It does not make sense to me that if I import a library that implements a +
operator for a type-class via an extension method, then I cannot define my own +
for a more specific type-class within the same namespace without having the library +
for fallback/different behavior when I need it.
Iâm not the only one. Past issues (#14777, #12680) show that others encountered the same problem, suggesting the same intuition.
I donât know how many libraries were completely converted to Scala 3, but I think that it is likely we havenât seen enough noise about this since many still use implicit classes for cross-compilation. If this behavior can be fixed, we should do so promptly, because it can lead to breaking changes as more code bases rely on extension methods.