Extension Function vs. Regular Function in Kotlin

Extension Function vs. Regular Function in Kotlin

I’m a big fan of extension functions in Kotlin, for me it’s another tool in our toolbox to give the developer additional options as an API designer or consumer, but this post is not about the philosophical discussion of the merits of extension functions, like the ability to chain utility functions vs. the issues that come with extension function, like having functions or methods that look like they are part of a well defined API but really are not. Both sides offer good arguments and probably deserve a separate post on its own to discuss about those, but this post is more about the technical/practical aspect of those 2 ways to write functions. We will explore one scenario where, instead of your personal preferences, the actual Kotlin’s type inference system will introduce enough friction to greatly influence your decision one way or the other.

A simple use case first…

We will start with a simple example of a log() regular function that accepts a 1 parameter higher-order function and just prints the function’s parameter and return value after invoking it (press the play button) :

For comparison basis, here is the same example as above but implemented with extension function instead (press the play button) :

Notice the receiver type ((T) -> R).log(), not only classes, but functions can also act as receiver type, pretty cool!

Where does regular functions shine vs. extension functions?

This is where it gets interesting! So far, as our add4 function is declared above, the higher-order function passed to our log() function has all its parameter and return types defined in advance. But what if we want to pass a generic function instead as our higher-order function to our log() function (press the play button) ?

The function reference ::doSomething gets all its types resolved when we declare the types of our func function. This is not a scenario that we should expect to happen very often, but this could happen in the case of e.g. our doSomething function above being a 3rd party library exposing a generic interface to a ORM to interact with databases. What should we expect now if we translate the example above to use extension function instead (press the play button) ?

We have a scenario where the compiler cannot infer the type of <T> (and <R>, remove T from <T, R> and replace p: T with p: String in doSomething to see), this seems to be a limitation in the usage of function reference with extension function. By giving up on using function reference and expanding it to its lambda expression representation, we can then help the compiler by assigning the missing types (press the play button) :

So extension function applied to a function reference that references a generic function (phew ;-)) seems to be one case where not using extension functions and sticking to regular functions (or java style static methods) is the only way to go. But if doSomething is not generic, extension function applied to function reference works just fine (press the play button) …

Conclusion

For probably 95% of the use cases, both regular and extension function options work just fine and it’s really a matter of personal preference. Most of the time it will really depend on the kind of experience we want to provide to the user of our code, but there are rare circumstances where the compiler “helps” you make that choice… at least as of Kotlin v1.3.50

%d bloggers like this: