Idiomatic Kotlin: Higher-order functions and Function Types
This article is a part of the Idiomatic Kotlin series. The complete list is at the bottom of the article.
In one of the previous articles, we talked about lambda expressions. This article is somewhat related and we will soon know why.
What is a higher order function?
A higher order function is a function that accepts or return another function. In Kotlin, functions are treated as first class citizens so you can assign them to variables and pass them to other functions as parameters. This can be achieved by using lambda expressions or function references.
Function Types
Kotlin uses function types to represent the type of a function (in contrast to functional interfaces in Java). The syntax of a function type is as follows:
(Type, Type) -> ReturnType
with the left side containing the parameter types and the right side the return type. For empty returns, you can use Unit.
Function types can be instantiated in a multitude of ways. Let’s look at some of them.
- Lambda expressions
val filter : (Int) -> Boolean = { it < 2 }
Notice that the filter variable is explicitly defined a type. This is optional if the compiler can infer the type from the lambda expression. As an example, this is equivalent to
val filter = { x : Int -> x < 2 }
2. Anonymous functions
Anonymous functions are function declarations without a name. The above filter function, when converted to an anonymous function would look something like this
val filter = fun(x : Int) : Boolean = x < 2
Just like lambda expressions, some types in the declaration can be inferred, simplifying your code.
3. Existing function
Member functions, top level functions, or extension functions can be used as an instance of a function type. As an example, getting a reference to the String.filter
function would be
val filter = String::filter
Any other function declarations, even constructors can be used.
Invoking function type instances
The syntax for invoking function type instances is pretty straightforward. You can use either
f.invoke(x)
or simply
f(x)
Why is there an invoke
method in a function type? We’ll know for sure in a while.
Function types under the hood
Function types, surprisingly (or not), is implemented as interfaces under the hood. Let’s take a look at the definition of a generic function type with 1 argument.
Therefore, when you instantiate a function type, you are actually creating an instance of the FunctionN
interface with the body of your function being called when the invoke
method is called. This approach is similar to Java functional interfaces. Therefore, you can call higher-order functions from Java side by implementing these FunctionN
interfaces (or using lambda when targeting Java 8).
The Kotlin standard library defines generic function type of up to 22 parameters, if that is useful for you.
Lambda Cost
Now that you are aware that lambda expressions are actually compiled as anonymous classes under the hood, you might already have an idea regarding the hidden cost of using them.
For every lambda expression defined, there will be a corresponding class definition for it. And if your function is capturing, there will exist an instance as well corresponding to the number of invocation. Luckily for us, there is a feature called inlining
that can help with this. Inlining will be the topic of the next article so stay tuned.
Check out the other articles in the idiomatic kotlin series. The sample source code for each article can be found here in Github.
- Extension Functions
- Sealed Classes
- Infix Functions
- Class Delegation
- Local functions
- Object and Singleton
- Sequences
- Lambdas and SAM constructors
- Lambdas with Receiver and DSL
- Elvis operator
- Property Delegates and Lazy
- Higher-order functions and Function Types
- Inline functions
- Lambdas and Control Flows
- Reified Parameters
- Noinline and Crossinline
- Variance
- Annotations and Reflection
- Annotation Processor and Code Generation