Lambda: What It Is, Syntax, and Usage in Kotlin

Author: IT Sectr Published: 2026-06-23 Reading time: 7 min

Lambda is an anonymous function represented as a compact expression { arguments -> body }. In Kotlin, lambdas are the primary way to pass behavior into higher-order functions, providing concise syntax without declaring a separate function. According to the Kotlin documentation (2026), over 70% of higher-order functions in typical projects are called with lambdas. Lambda enables writing code in a functional style with minimal syntactic noise.

Key Takeaways

  • Lambda — an anonymous function in { args -> body } syntax, without a name
  • Implicit it replaces the single argument of a lambda, simplifying syntax
  • Trailing lambda — moving the last lambda outside the call parentheses for readability
  • Closure — a lambda can access variables from the outer scope
  • Functional type (T) -> R defines the lambda signature and is checked by the compiler

What is lambda?

Lambda (lambda expression) is an anonymous function defined as an expression rather than a declaration. The term originates from Alonzo Church’s lambda calculus (1936). In programming, a lambda is a block of code that can be passed as a value, stored in a variable, and called later. In Kotlin, a lambda is always enclosed in curly braces { }, with arguments separated from the body by the -> arrow.

A lambda differs from a regular function by the absence of a name and the fun keyword. This allows declaring behavior directly at the point of use without cluttering the code with additional declarations. The Kotlin compiler infers argument types from context, so in many cases types can be omitted. A lambda is syntactic sugar over the functional type (T) -> R: every lambda has a corresponding functional type.

The primary use of lambdas in Kotlin is passing them to higher-order functions. The standard library uses lambdas everywhere: filter { it > 0 }, map { it.uppercase() }, forEach { println(it) }. Lambdas are also used for event listeners, callbacks, and object configurators through apply and run, making code more declarative compared to anonymous classes.

Lambda expression syntax in Kotlin

The full form of a lambda expression in Kotlin: { arguments -> lambda_body }. Arguments are listed separated by commas, followed by an arrow and the body — one or more expressions. If the body contains multiple lines, the last expression becomes the return value. If a function takes a lambda as its last argument, it can be moved outside the call parentheses.

kotlin
val sum: (Int, Int) -> Int = { a, b -> a + b }

// Types inferred from context
val sumShort = { a: Int, b: Int -> a + b }

// Lambda call
val result = sum(5, 3)  // 8
println(result)

The variable sum is declared with an explicit functional type (Int, Int) -> Int and initialized with a lambda { a, b -> a + b }. For sumShort, types are specified inside the lambda, and the variable type is inferred. Both variants are equivalent. Calling a lambda is no different from calling a regular function — using the sum(5, 3) syntax. A lambda without arguments is written as { body }, with one argument — { it -> it * 2 } or simply { it * 2 }.

Implicit it and omitting arguments

When a lambda takes exactly one argument, Kotlin provides the implicit name it. This eliminates the need to declare the argument on the left side. The developer writes only the lambda body, using it to access the single parameter. This syntax is especially convenient in collection processing chains, where it refers to the current element.

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

// Explicit parameter name
val squaredExplicit = numbers.map { n -> n * n }

// Implicit it
val squared = numbers.map { it * it }

// Filtering with it
val even = numbers.filter { it % 2 == 0 }

println(squared) // [1, 4, 9, 16, 25]

In the example, map { it * it } uses it instead of declaring n ->. Kotlin automatically assigns it for a single lambda parameter. If a lambda takes two or more arguments, it is not created — all parameters must be declared explicitly. For better readability of nested lambdas, it is worth giving parameters meaningful names and using it only in short one-line predicates.

Trailing lambda and multiline lambdas

Trailing lambda is a Kotlin syntax feature that allows moving the last lambda argument outside the function call parentheses. This makes the code resemble built-in language constructs. If the lambda is the only argument, the parentheses can be omitted entirely. This approach is actively used in the standard library and DSLs.

kotlin
// Standard call
thread(block = {
    println("Hello from thread")
})

// Trailing lambda version
thread {
    println("Hello from thread")
}

// repeat with trailing lambda
repeat(3) { index ->
    println("Iteration $index")
}

// run with multiline lambda
val config = run {
    val host = "localhost"
    val port = 8080
    "$host:$port"
}

The thread function accepts a Runnable as the last argument. Thanks to trailing lambda, the lambda can be passed outside the parentheses, visually resembling a code block. The repeat function takes a number of iterations and a lambda with an index. run is an example of a higher-order function where a multiline lambda computes a value returned by the last expression. The parentheses around run() can be omitted if the lambda is the only argument.

Variable capture (closure) in lambdas

Variable capture (closure) is the ability of a lambda to access variables declared in the outer scope. In Kotlin, a lambda can read and modify var variables, as well as read val variables from the surrounding context. Captured variables are stored with the lambda throughout its lifecycle, enabling functions with state.

kotlin
fun makeCounter(): () -> Int {
    var count = 0
    return {
        count++  // captures count
    }
}

fun main() {
    val counter = makeCounter()
    println(counter()) // 0
    println(counter()) // 1
    println(counter()) // 2
}

The makeCounter function declares a local variable count and returns a lambda { count++ }. Even though makeCounter has finished execution, the returned lambda retains a reference to count through the closure. Each call to counter() increments and returns the current value. The closure mechanism underlies currying, function factories, and memoization. It is important to remember that capturing mutable variables can lead to unexpected behavior in concurrent execution.

Frequently Asked Questions

Can a lambda have a return?

Lambda does not support unconditional return — a return without a label returns control to the outer function. To exit a lambda, use return@label, where label is the name of the higher-order function the lambda was passed to.

What is the difference between a lambda and an anonymous function?

An anonymous function is written as fun(a: Int): Int { return a * 2 } and supports return without a label. A lambda { a -> a * 2 } is shorter in syntax but requires return@label for early exit.

How to pass a lambda as an argument without an arrow?

If a lambda takes no arguments, the arrow is omitted: { body() }. If it takes one argument but doesn’t use it, write { _ -> body() } with an underscore to ignore the parameter.

Does a lambda always create a new object?

Yes, every lambda passed to a non-inline function creates an instance of an anonymous class. For inline functions, the lambda is inlined at the call site without creating an object. Non-capturing lambdas are cached as singleton objects.

Why can’t return be used directly in a lambda?

Return in Kotlin is a jump operator belonging to the nearest function declared with fun. A lambda is not a function (fun), but an expression. Therefore, a return inside a lambda without a label is treated as returning from the outer enclosing function.

Summary

  • Lambda — an anonymous function in { args -> body } syntax, passed as a value to higher-order functions
  • Implicit it replaces the single argument, making code more compact in short predicates
  • Trailing lambda moves the last lambda outside the call parentheses, reading like a code block
  • Closure allows a lambda to access variables from the outer context after the enclosing function has completed
  • Functional type (T) -> R defines the lambda signature with compile-time checking
  • Inline functions eliminate anonymous class creation for lambdas, improving performance in loops
  • Multiline lambdas return the value of the last expression, supporting complex computations without declaring a separate function

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also