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 [email protected]. I’d greatly appreciate your feedback! :)