See my comment here: https://news.ycombinator.com/item?id=14365317
fun String.shout() = this + "!!!"
fun shout(s: String) = s + "!!!"
Looks pretty much the same to me. Extension functions use static dispatch so what's the point of using member syntax?Also, extension functions can be overwritten by a member function with the same signature. That seems quite fragile to me considering the class and the extension function very likely have different authors.
That said, I don't know Kotlin very well. So perhaps you can set me straigt on any misconceptions I may have.
I code Elixir a lot these days and spend a lot of time looking at hexdocs.pm and occasional library source code. With Kotlin or C#, this is seldomly necessary, because the IDE's autocomplete tells you everything you can do with this object. It doesn't guess, it knows. If it's in the list, you can do it, if it's not, you can't.
Now, if I have a string object called "str", and I write "str.", I want to see a list of everything I can do with it. But a MyStringUtils.shout() is never in that list, because it does not come after the dot.
With extension functions, this becomes possible with userland extensions to existing classes and interfaces. All you need is a single import on top, and good IDEs (eg. visual studio) can also auto-import that one if you use a function from it once.
(1) Last I tried, Scala was too slow to compile to provide 100% perfect autocomplete within a few milliseconds. IntelliJ did some Python-esque guesswork to mitigate that, but that effectively reduces the feature to "save me some keystrokes", i.e. not very useful. Maybe this has been fixed since.
Example: I have a layout-algorithm for a grid that takes sections:
interface Section {
val title: String
fun something()
}
Then I have the grid-algorithm. Inside I store expanded/collapsed state for each section. When I use a member-extension with both receivers I can write that pretty conveniently: class GridLayout(sections: List<Section>) {
/* the state */
val expandedSections = mutableSetOf<Section>()
/* the extensions I need in this context */
val Section.isExpanded get() = this@Section in expandedSections
fun Section.expand() = expandedSections += this@Section
fun compute() {
...
if (!section1.isExpanded) {
section2.expand()
}
...
}
}So the difference would be in Kotlin
object StringUtilClass() {
fun shout(s: String) = s + "!!!""
}
import StringUtilClass.*
StringUtilClass.shout("I'm mad")
vs fun String.shout() = this + "!!!"
import StringExtensions.*
"I'm mad".shout()
Kotlin doesn't always add brand new functionality, it also strives to make it faster to read and write code. import StringUtilClass.*
shout("I'm mad")