Well, that could be, but isn’t this conceptually the cleanest solution? What, realistically, are the options? Especially in a way that is TASTY-compatible?
(1) Status quo: extension methods effectively have no namespace. They clobber each other willy-nilly like everything without a namespace always does, getting quadratically bad the more they are used. Solution: style guide says “never use extension methods instead of implicit classes when writing libraries”. For something that was supposed to replace implicit classes (and has considerably nicer syntax), this is pretty bad.
(2) Generate a synthetic erased implicit class, and use the regular implicit mechanism: when you see x.foo it is code for (new org.whomever.package$Filename$4(x)).foo , where this is the 4th extension in package package from file Filename. Or something like that. Anyway, then you have replaced the implicit class mechanism, which works, with a better-hidden variant of itself.
(3) Allow ad-hoc overloading based on imports at least for extension methods, if not everything. The compiler already figures out the options to generate a workable error message. So, yes, you have a new idea of overloaded dispatch where foo(x) might be bar.baz.foo(x) or bippy.quux.foo(x) depending on whether the type of x is knowable and resolves to one or the other. If you want to fit it into the existing MultiDenotation framework, then you would need to create a synthetic type equivalent to
trait SyntheticMultiDenotation$8 {
inline def foo(x: String): Int = bar.baz.foo(x)
inline def foo(f: Float): Long = bippy.quux.foo(x)
}
and then import of the two extension methods with foo would be equivalent to asking for the two existing foo methods to not be imported, and instead creating an instance of such a trait (or an equivalent object which forwards the extensions) and importing those foos instead. If the synthetic trait or object could not be created, then you’d get a compiler error.
(4) Allow more of the namespace to be specified. Very clunky, but at least you can still use method notation. So, x.foo doesn’t work, but x.baz.foo and x.quux.foo do (assuming no ambiguity on baz or quux). Unfortunately, this would introduce a source of name clashes–full paths vs. method names–that never used to exist. But since it would only be invoked when the class itself didn’t have the method, maybe it would be okay.
(5) Exploit the targetName mechanism for disambiguation, with part of the desugaring of x.foo being resolving foo(x) to stringOverloadFoo(x). I’m not sure that this is actually a solution, because to me it seems like all it does is allow overloads with types that are different when seen by Scala (which should be okay) but not Java (oops), so maybe the compiler can’t use this to invent overloads where none exist.
Maybe there are other options. But to me, it seems like ad-hoc overloading (for extension methods if nothing else) is conceptually the cleanest. There is one place it doesn’t work: when the extensions are defined in different files in the same namespace. Then there is literally no way to disambiguate the two. But that could be worked around with either @targetName or simply saying: sorry guys, but if you really want to do that, you’ve got to stuff it all in the same file. It’s your namespace, so you ought to have that much control, even if it isn’t the nicest organization. (We already have compromises to make in that regard with things in objects, though actually export reduces that problem substantially.)