Skip to main content

Kotlin Higher Order Functions

Introduction​

Higher-order functions are a powerful feature in Kotlin that enables functional programming paradigms. A higher-order function is a function that either:

  • Takes one or more functions as parameters, or
  • Returns a function as its result, or
  • Both of the above

This concept might seem advanced, but once you understand it, higher-order functions help you write more concise, flexible, and reusable code. They are particularly useful for operations like filtering, mapping, or transforming collections.

Understanding Higher Order Functions​

Functions as Parameters​

In Kotlin, we can pass functions as arguments to other functions. This is possible because functions are first-class citizens, meaning they can be treated like any other value.

Here's a simple example:

kotlin
// A higher-order function that takes a function as a parameter
fun operateOnNumbers(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}

fun main() {
// Passing a lambda function as an argument
val sum = operateOnNumbers(5, 3) { x, y -> x + y }
println("Sum: $sum") // Output: Sum: 8

// Another lambda for multiplication
val product = operateOnNumbers(5, 3) { x, y -> x * y }
println("Product: $product") // Output: Product: 15
}

In this example:

  • operateOnNumbers is a higher-order function that takes two integers and a function as parameters
  • The function parameter has type (Int, Int) -> Int, meaning it takes two integers and returns an integer
  • We pass different lambda expressions to perform different operations with the same higher-order function

Function Types​

In Kotlin, functions have types. The syntax for a function type is:

kotlin
(ParameterType1, ParameterType2, ...) -> ReturnType

For example:

  • () -> Unit: A function that takes no parameters and returns nothing
  • (Int) -> Int: A function that takes an integer and returns an integer
  • (String, Int) -> Boolean: A function that takes a string and an integer and returns a boolean

Returning Functions​

Higher-order functions can also return other functions:

kotlin
// A higher-order function that returns a function
fun createMultiplier(factor: Int): (Int) -> Int {
return { number -> number * factor }
}

fun main() {
val double = createMultiplier(2)
val triple = createMultiplier(3)

println("Double of 5: ${double(5)}") // Output: Double of 5: 10
println("Triple of 5: ${triple(5)}") // Output: Triple of 5: 15
}

In this example, createMultiplier returns a function that multiplies its input by a specified factor.

Practical Applications​

Working with Collections​

Higher-order functions shine when working with collections. Kotlin's standard library provides many useful higher-order functions like filter, map, and reduce.

Example: Filter​

kotlin
fun main() {
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

// Filter out odd numbers
val evenNumbers = numbers.filter { it % 2 == 0 }
println("Even numbers: $evenNumbers")
// Output: Even numbers: [2, 4, 6, 8, 10]
}

Example: Map​

kotlin
fun main() {
val numbers = listOf(1, 2, 3, 4, 5)

// Square each number
val squared = numbers.map { it * it }
println("Squared numbers: $squared")
// Output: Squared numbers: [1, 4, 9, 16, 25]
}

Example: Reduce​

kotlin
fun main() {
val numbers = listOf(1, 2, 3, 4, 5)

// Calculate sum using reduce
val sum = numbers.reduce { acc, number -> acc + number }
println("Sum: $sum") // Output: Sum: 15

// Calculate product using reduce
val product = numbers.reduce { acc, number -> acc * number }
println("Product: $product") // Output: Product: 120
}

Custom Iterators​

You can create custom iterators using higher-order functions:

kotlin
fun <T> Collection<T>.forEachWithIndex(action: (index: Int, T) -> Unit) {
var index = 0
for (item in this) {
action(index++, item)
}
}

fun main() {
val fruits = listOf("Apple", "Banana", "Cherry")

fruits.forEachWithIndex { index, fruit ->
println("Item #$index: $fruit")
}
// Output:
// Item #0: Apple
// Item #1: Banana
// Item #2: Cherry
}

Real-world Example: Creating a Simple DSL​

Higher-order functions can be used to create domain-specific languages (DSLs):

kotlin
data class Person(var name: String = "", var age: Int = 0)

fun person(personInit: Person.() -> Unit): Person {
val person = Person()
person.personInit()
return person
}

fun main() {
val john = person {
name = "John"
age = 30
}

println("Created person: ${john.name}, ${john.age}")
// Output: Created person: John, 30
}

This example demonstrates how higher-order functions enable expressive code that almost reads like a specialized language for creating Person objects.

Function References​

In addition to lambda expressions, you can use function references to pass existing functions:

kotlin
fun isEven(number: Int): Boolean {
return number % 2 == 0
}

fun main() {
val numbers = listOf(1, 2, 3, 4, 5)

// Using a function reference with ::
val evenNumbers = numbers.filter(::isEven)
println("Even numbers: $evenNumbers")
// Output: Even numbers: [2, 4]
}

Using inline Functions for Better Performance​

When using higher-order functions, there's a runtime overhead due to creating function objects. To mitigate this, Kotlin provides the inline keyword:

kotlin
// An inline higher-order function
inline fun measureTimeMillis(block: () -> Unit): Long {
val start = System.currentTimeMillis()
block()
return System.currentTimeMillis() - start
}

fun main() {
val time = measureTimeMillis {
// Pretend this is a long operation
for (i in 1..1000000) {
// Do something
}
}
println("Operation took $time ms")
}

The inline keyword tells the compiler to insert the function's bytecode directly at the call site, eliminating the overhead of function objects and improving performance for small, frequently called higher-order functions.

Summary​

Higher-order functions are a fundamental concept in Kotlin that enable functional programming techniques:

  • They can take functions as parameters or return functions
  • They allow for more concise, flexible, and reusable code
  • They're especially useful for collection operations and creating DSLs
  • Function references and lambdas can be used as arguments
  • The inline keyword can improve performance for small higher-order functions

By mastering higher-order functions, you can write more expressive and efficient Kotlin code that solves complex problems with less boilerplate.

Further Practice​

Exercises​

  1. Create a higher-order function that takes a list of strings and a function that transforms each string, then returns the transformed list.
  2. Write a function that returns another function that adds a prefix to strings.
  3. Implement your own version of the filter function that works on lists.
  4. Create a simple DSL for building HTML using higher-order functions.

Additional Resources​

Happy coding with Kotlin's higher-order functions!



If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)