Extension functions, again. I feel this is one of the most unexplored topics of Kotlin (but I’m trying!). There are barely any standards for using them, but here I want to focus on one thing - should we choose extension functions over standalone functions?

This post is inspired by yet another talk/article which praises extension functions. “They are cool!”, “Look, this code looks much better!” etc.

I get it, it looks really cool. But maybe it’s mostly because of novelty? And “looking cool” is really subjective, right? I actually think that “looking cool” would be valid argument, if not for the fact that it’s not objective.

Let’s see an example

  • with extension function
1337.toHexString()
  • with standalone function
toHexString(1337)

Of course it’s very simple example, but somehow I see the first one as a bit more readable. And because how most people would read this line of code in their heads we could - to some extent - make the case that first example is “universally” more readable.

Especially for two arguments function, where one argument can be converted as extended class, that makes it look significantly better:

  • with extension function
file.moveTo(dir)
  • with standalone function
moveTo(file, dir)
// or
moveFileToDir(file, dir)

But let’s see another example

  • with extension function
1337.increment()
  • with standalone function
increment(1337)

Hopefully here you can see that static/standalone function can be at least as readable, or even mode readable than extension function. As always - it depends. Depends on semantics, usage and probably several other things.

So what about this “case”?

In short - we cannot simply say that extension function looks better. This is not absolute truth. And clear rules for when one is more readable than the other would be pretty hard to make up (but feel free to try!). That’s the problem with readability - it depends on many things.

This was just to say that many people are only expressing their opinions in given contexts. I’d really hope that extension functions could give me something more than merely potential improvement in readability.

There is still hope! While I couldn’t really decide on my own when to use which (but I definitely know how extension functions can be abused) I found one good reason to favour extension functions. It is…

Scoped Discoverability!

That’s what makes a difference!

Let’s have a project when we need to transform Int to its hexadecimal string representation pretty often. We obviously would like to have common function to do that. Oh, and in this project we have a let of hex stuff, so getting hex representation from bytes, string, input stream, etc.

Let’s say we have something like this, but bigger and with more variety:

fun toHexString(n: Int): String { (...) }

fun toHexString(bytes: ByteArray): String { (...) }

fun toHexString(inputStream: InputStream): String { (...) }

fun toHexString(long: Long): String { (...) }

Now, in another distant file you have Int and you want to transform it into hex format - but you don’t remember function name. Well… have fun! (get it? fun!) Kotlin has A LOT of standalone functions available from everywhere!

Intellij autocomplete EVERYTHING

Let’s go back and substitute functions with extension functions

fun Int.toHexString(): String { (...) }

fun ByteArray.toHexString(): String { (...) }

fun InputStream.toHexString(): String { (...) }

fun Long.toHexString(): String { (...) }

Now, in another distant file you have integer and you want to transform it into hex format - but you don’t remember function name. Well… there is hope! Just write your interger and try Intellij autocomplete:

Intellij autocomplete Int

of course this function is not the first suggestion, but it is among these suggestions:

Intellij autocomplete found!

And believe me, the list for Int is much much shorter than list for standalone functions.

That’s why I call it “scoped discoverability” - you are scoping discovery of possible functions because you provide type on which function should be available. Even though extension functions are just regular static methods under the hood, you can’t do that with regular static method. This is equivalent of asking Intellij “please suggest me all functions that have Int as first argument”. There is no easy way to do that, but with extension functions, that’s very easy.

Is it dealbreaker? Definitely not. If you think that standalone function or function inside an object is better and you (and your teammates) have already learned to work this way, take it easy. Maybe just the next time you want to use a function and don’t remember the name you might think if extension function would make your life a bit easier.