Skip to content
AgileLogics
7 min read

Lambdas and Higher-Order Functions

Prerequisites: Default and named arguments

A lambda is a function without a name, written inline, that you can store in a variable or pass to another function. A function that takes or returns another function is a higher-order function.

Real-world analogy: a lambda is an instruction slip you hand to someone along with a job — "for each parcel, check the weight like this". They decide when to follow it.

Syntax

fun main() {
    val double = { n: Int -> n * 2 }
    val add: (Int, Int) -> Int = { a, b -> a + b }

    println(double(21))
    println(add(2, 3))
}
42
5
  • Parameters come before ->; the body after it.
  • The last expression in the body is the result — no return.
  • (Int, Int) -> Int is a function type: "takes two Ints, returns an Int".

it

When a lambda has exactly one parameter, you can leave it out and refer to it as it:

fun main() {
    val shout: (String) -> String = { it.uppercase() + "!" }
    println(shout("hi"))
}
HI!

Passing lambdas to functions

If a function's last parameter is a function, the lambda can go outside the parentheses — a trailing lambda. If it's the only argument, the parentheses can go entirely:

fun repeatTask(times: Int, task: (Int) -> Unit) {
    for (i in 1..times) task(i)
}

fun main() {
    repeatTask(3) { pass ->
        println("Pass $pass")
    }

    val names = listOf("Linus", "ada", "Grace")
    println(names.sortedBy { it.lowercase() })
}
Pass 1
Pass 2
Pass 3
[ada, Grace, Linus]

Trailing lambdas are everywhere in Kotlin — collection functions, Android's Jetpack Compose, Gradle build files — which is why Kotlin code so often reads like a built-in language feature.

Function references

:: refers to an existing function, so you can pass it like a lambda:

fun isLong(word: String) = word.length > 4

fun main() {
    val words = listOf("fig", "banana", "kiwi", "cherry")
    println(words.filter(::isLong))
}
[banana, cherry]

Capturing variables

Lambdas can read and change variables from the surrounding code:

fun main() {
    var total = 0
    listOf(5, 10, 15).forEach { total += it }
    println(total)
}
30

Common mistakes

  • Writing return inside a lambda passed to forEach and expecting it to skip one item — a plain return returns from the whole enclosing function. Use return@forEach to skip to the next item.
  • Overusing it in nested lambdas, where it becomes unclear which it is which. Name the parameters instead.

Practice

Sort listOf("banana", "fig", "apple") by word length, shortest first, using a trailing lambda.

Show solution
fun main() {
    val words = listOf("banana", "fig", "apple")
    println(words.sortedBy { it.length })
}
[fig, apple, banana]