You’re talking about inconsistency between two very different mechanisms. It doesn’t need a lot of effort to find multiple (potential?) inconsistences, e.g.:
- there’s case object and case class, but no case trait - inconsistency
- you can do
import stableIdentifier._
but can’t do import unstableIdentifier._
- inconsistency
- you can have class parameters, soon have trait parameters, but no object parameters in plan - inconsistency (this is actually absurd one, but still there’s some kind of inconsistency here)
- you can apply
var
to constructor parameters but not to method parameters - inconsistency
- you can import from any stable identifier but when you save stable identifier into a new variable it may not be a package (i.e. you can do
val newSomething = stableIdentifier
for any stable identifier that is not a package) - inconsistency
- you need
value.type
to get type of non-literal, but for literals you don’t need that .type
suffix - inconsistency
- etc
Shall we solve all of them? Maybe, maybe not, we need to consider the consequences, the need for them, how they fit in the language (are they consistent with spirit of Scala?), etc
Sometimes this.member(args)
is the same as member(args)
but not always. Sometimes one form compiles and other don’t. Sometimes both compile, but refer to something different. That’s because there’s some overlap between them, but they are in fact governed by completely different rules.
A big counterargument for extension methods on this
is that extension methods are mostly useful for achieving ad-hoc polymorphism for types you don’t control. E.g. you can enrich java.util.String
only with extension methods (Dotty ones or Scala 2 ones, whatever). You can’t mix in additional traits to java.util.String
as you don’t have control over it, i.e. you can’t edit it and add directly the methods you want. But (in current version of Scala) if you write code that uses this
then this means you have full control over the class of which this
is the instance. Therefore it’s much more natural to just add extra traits to that class instead of going through the contortions of adding extension methods.
Instead of this (extension methods on class you control):
class X(val v: Int) {
def hello(): Unit = {
this.extensionMethod()
// extensionMethod() // alternative potential syntax
}
}
implicit class RichX(x: X) {
def extensionMethod(): Unit = println(x.v + 5)
}
you write this (ordinary OOP mixins):
class Y(val v: Int) extends Mixin {
def hello(): Unit = {
methodFromMixin()
}
}
trait Mixin { this: Y =>
def methodFromMixin(): Unit = println(v + 5)
}
In order for extension methods on this
to make more sense, this
would have to be of foreign type you don’t control, e.g.
library.method { this => // rebinding 'this' to an instance of foreign type
// now this makes sense as we can't mixin anything to 'this'
this.extensionMethod()
// or the more concise but confusing syntax
extensionMethod()
}
But how often such thing would happen in idiomatic Scala code? I think very rarely (but can be wrong here).
Kotlin is heavily oriented toward integrating with Java libraries and frameworks thus Kotlin programmers are often forced to deal with deficient Java APIs and ad-hoc extending them makes a lot of sense. OTOH, Scala APIs are usually pretty rich out of the box.