Lambdas & Higher-Order Functions

Reviewed & published by Brayan K

Kotlin is a modern, concise language that treats functions as values — you can write anonymous lambdas and pass them into higher-order functions, which is the heart of its expressive style.

Part of the free Kotlin course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

By the end of this lesson you'll write lambdas, use the implicit it parameter, and create functions that accept other functions.

What You'll Learn in This Lesson

1️⃣ Lambdas

A lambda is an anonymous function written as { params -> body }. You can store it in a variable, whose type is a function type like (Int) -> Int. When a lambda has a single parameter you can refer to it as it. The last expression is the return value.

fun main() {
    // A lambda is an anonymous function in { params -> body }.
    val square = { n: Int -> n * n }
    println(square(5))                    // -> 25

    // The type is (Int) -> Int : takes an Int, returns an Int.
    val add: (Int, Int) -> Int = { a, b -> a + b }
    println(add(3, 4))                     // -> 7

    // When a lambda has ONE parameter, you can use 'it' implicitly.
    val isEven: (Int) -> Boolean = { it % 2 == 0 }
    println(isEven(6))                     // -> true

    // The last expression in a lambda is its return value.
    val describe = { n: Int ->
        val parity = if (n % 2 == 0) "even" else "odd"
        "$n is $parity"                   // returned
    }
    println(describe(7))                   // -> 7 is odd
}

Storing behaviour in a value like this is the gateway to functional programming — you can pass these around just like numbers or strings.

2️⃣ Higher-Order Functions

A higher-order function accepts a function as a parameter (or returns one). The parameter's type is a function type. Because the function is the last parameter, you can use trailing-lambda syntax and place the lambda outside the parentheses.

// A HIGHER-ORDER function takes (or returns) another function.
// Here 'operation' is a parameter whose type is (Int, Int) -> Int.
fun compute(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

fun main() {
    println(compute(6, 2) { x, y -> x + y })   // -> 8
    println(compute(6, 2) { x, y -> x - y })   // -> 4
    println(compute(6, 2) { x, y -> x * y })   // -> 12

    // Trailing-lambda syntax: when the lambda is the LAST argument,
    // it can go OUTSIDE the parentheses — that's the { } above.

    // Collections use higher-order functions everywhere:
    val nums = listOf(1, 2, 3, 4)
    nums.forEach { println("n = $it") }
}

The same compute function did addition, subtraction, and multiplication just by changing the lambda. That's the power of abstracting over behaviour.

Your turn. Replace the TODO, then run and compare.

// 🎯 YOUR TURN — replace each TODO then run main()
fun main() {
    // 1) A lambda that triples a number. Use 'it' for the single param.
    val triple: (Int) -> Int = { TODO * 3 }   // 👉 replace TODO with 'it'
    println(triple(4))                        // expect 12

    // 2) A higher-order function that applies a function twice.
    fun twice(x: Int, f: (Int) -> Int): Int = f(f(x))

    // Pass the triple lambda in using trailing-lambda syntax:
    println(twice(2) { it * 3 })              // 2 -> 6 -> 18

    // ✅ Expected:
    //    12
    //    18
}

Mini-Challenge: A Configurable Greeter

Write a higher-order function that delegates formatting to a lambda, then call it with two different formatters.

// 🎯 MINI-CHALLENGE: a configurable greeter.
// Save as Greeter.kt and run main().
//
// 1. Write fun makeGreeting(name: String, formatter: (String) -> String): String
//    that returns formatter(name).
// 2. In main(), call it twice:
//    - formatter that returns "Hello, <name>!"
//    - formatter that returns the name in UPPERCASE
// 3. Print both results.
//
// ✅ Example output:
//    Hello, Sam!
//    SAM

fun main() {
    // your code here
}

Common Errors (and the fix)

Pro Tips

📋 Quick Reference — Lambdas

ConceptKotlin Syntax
Lambda{ n -> n * n }
Function type(Int) -> Int
Implicit param{ it * 2 }
HOF parameterf: (Int) -> Int
Trailing lambdacompute(6, 2) { x, y -> x + y }

🎉 Lesson Complete!

Practice quiz

What is a lambda in Kotlin?

  • A named top-level function
  • An anonymous function written as { params -> body }
  • A class with one method
  • A reserved keyword

Answer: An anonymous function written as { params -> body }. A lambda is an anonymous function literal written as { params -> body }.

What is the type of the lambda { n: Int -> n * n }?

  • Int
  • (Int) -> Int
  • () -> Int
  • (Int, Int) -> Int

Answer: (Int) -> Int. It takes one Int and returns an Int, so its type is (Int) -> Int.

What is the return value of a lambda?

  • Always Unit
  • The first expression
  • Whatever follows the return keyword
  • Its last expression

Answer: Its last expression. A lambda returns the value of its last expression.

In a single-parameter lambda, what implicit name refers to the argument?

  • it
  • arg
  • this
  • value

Answer: it. A single-parameter lambda can use the implicit name it.

What is a higher-order function?

  • A function with many parameters
  • A function defined at file top level
  • A function that takes or returns another function
  • A recursive function

Answer: A function that takes or returns another function. A higher-order function takes a function as a parameter, returns one, or both.

When can you use trailing-lambda syntax?

  • When the function's last parameter is a function type
  • Only with no other arguments
  • When the lambda is first
  • Never with collections

Answer: When the function's last parameter is a function type. If the last parameter is a function type, the lambda can go outside the parentheses.

What does compute(6, 2) { x, y -> x + y } evaluate to in the lesson?

  • 12
  • 4
  • 8
  • 62

Answer: 8. The lambda adds: 6 + 2 = 8.

Why must a two-parameter lambda name its parameters?

  • it only works for a single parameter
  • Lambdas can't take two parameters
  • Naming is optional but recommended
  • To avoid recursion

Answer: it only works for a single parameter. it exists only for one parameter; with two you must name them, like { a, b -> ... }.

What does a bare 'return' inside a lambda try to do?

  • Return the lambda's last value
  • Exit the enclosing function
  • Restart the lambda
  • Return null

Answer: Exit the enclosing function. A bare return targets the enclosing function; a lambda's value is its last expression.

Which method reference replaces { it.uppercase() } in list.map?

  • uppercase::String
  • ::uppercase
  • String::uppercase
  • it::uppercase

Answer: String::uppercase. String::uppercase is a method reference equivalent to { it.uppercase() }.

Continue this course

Frequently asked questions

What exactly is a lambda?

A lambda is a function with no name, written inline as { parameters -> body }. It is a value you can store in a variable, pass to another function, or return. For example { n: Int -> n * n } is a lambda that squares its input. Lambdas are the foundation of Kotlin's expressive collection operations and its many scope and builder functions.

What is the 'it' keyword in a lambda?

When a lambda has exactly one parameter, Kotlin lets you refer to that parameter implicitly as it, so you can skip declaring it. For instance list.filter { it > 3 } means 'keep each element where the element is greater than 3'. it is concise and idiomatic, but for clarity with nested lambdas you may prefer to name the parameter explicitly.

What is a higher-order function?

A higher-order function is one that takes a function as a parameter, returns a function, or both. Kotlin treats functions as first-class values, so this is natural. Collection methods like map, filter, and forEach are higher-order functions: you pass them a lambda describing what to do with each element. Writing your own higher-order functions lets you abstract over behaviour, not just data.

Why can I put the lambda outside the parentheses?

This is the trailing-lambda convention: if the last parameter of a function is a function type, you can move that lambda outside the call's parentheses. So instead of compute(6, 2, { x, y -> x + y }) you write compute(6, 2) { x, y -> x + y }. If the lambda is the only argument, you can drop the parentheses entirely, as in list.forEach { ... }. It makes Kotlin DSLs and collection chains read beautifully.