@som-snytt - I was hoping that manufactured names would get around the arbitrary limitations of overloaded methods.
Under the current rules, for instance, mathematical types are incredibly annoying to use with extension methods.
For example, suppose you have a Vec
class. You want to scale your vector, so you add a method
def *(scale: Double): Vec = ???
Okay, now you can write v * 5
. Great! But what about 5 * v
? No worries,
extension (scale: Double)
def *(v: Vec): Vec = v * scale
Great! Now, let’s suppose we want to represent a value with some error. We’ll call it Approx. We want to be able to scale it up and down.
def *(scale: Double): Approx = ???
Now a * 5
works. Yay! And of course
extension (scale: Double)
def *(a: Approx): Approx = a * scale
And BOOM! Now neither 5 * v
nor 5 * a
work.
So, OKAY, okay, you either put things into different namespaces (now they work again), or you factor out the extension of double into its own file and
// ImplementationRestrictedDoubleExtensions.scala
extension (scale: Double) {
def *(v: Vec): Vec = v * scale
def *(a: Approx): Approx = a * scale
}
Now it works! Well, almost. Not if Approx
and Vec
are actually both opaque types over (Double, Double)
. But then @targetName
to the rescue, and it works again.
The shadowing problem described here is just one piece of this larger issue–method overloading and shadowing rules clobber the ability of extension methods to naturally extend things. It doesn’t “just work”; there are finicky rules that you constantly have to keep in mind, some of which won’t even show up at compile-time because the ambiguity doesn’t appear until the use-site (hope you wrote good unit tests!).
But, of course, since the implicit class method could resolve all this stuff completely fine, the issue is a manufactured one: it’s not that it isn’t 1000% clear what to do, it’s that the encoding (with particular method names) and limitations on overloaded methods end up getting in the way. With a target name, the limitation goes away, so everything could work naturally.
Except, right now, it only sometimes does.
Does this mean that you should be able to refer to the target name? I don’t know for sure. I tend to think of it as an implementation detail, so I’d argue no–but it’s an implementation detail that you stick in to avoid other implementation details mucking up your ability to write code in the natural way.