skip to content

Trailing-Lambda Convention

When a lambda is the last argument it moves outside the parentheses, and if it is the only argument the parentheses disappear entirely. This one convention is why Kotlin DSLs and functions like repeat read like built-in syntax.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What is the trailing-lambda convention in Kotlin, and how do you write `list.filter { it > 0 }` using it?

level: juniorimportance: must knowfreq 80%

answer

  1. Last param is a function → move lambda out
  2. filter({...}) → filter { ... }
  3. Empty () can be dropped
  4. Only the final argument moves out
  5. Makes higher-order calls read like keywords

basics

~20 s

If the last argument to a function is a lambda, you can write it outside the parentheses. So instead of filter({ it > 0 }) you write filter { it > 0 }, which reads more cleanly.

solid answer

~40 s

Kotlin's trailing-lambda convention lets you move a lambda that is the last parameter of a function out of the call's parentheses and place it in braces afterward. `list.filter({ it > 0 })` becomes `list.filter { it > 0 }`. This is purely syntactic sugar enabled by the fact that the lambda is the final argument. If the function takes other arguments, they stay inside the parentheses and only the trailing lambda moves out: `list.fold(0) { acc, x -> acc + x }`. The rule is what makes higher-order functions like `filter`, `map`, `forEach`, and DSL builders read like language constructs rather than ordinary calls. It is the single most pervasive piece of Kotlin syntax for functional code, so candidates must recognize it instantly.

code

kotlin · 6 lines
kotlin
val nums = listOf(-2, 3, -1, 5)
// full form
val a = nums.filter({ n -> n > 0 })
// trailing lambda + implicit it
val b = nums.filter { it > 0 }
println(b) // [3, 5]

go deeper

for a junior

Recognizes the syntax and can rewrite a call between the parenthesized and trailing forms.

for a middle

Knows it only applies to the last parameter and keeps other arguments inside the parentheses.

for a senior

Explains it is pure syntactic sugar that powers DSLs and reads like control flow; distinguishes it from implicit it.

for a principal

Frames it as an API-design lever: putting the function type last so callers get clean trailing-lambda call sites.

## The convention A **lambda** is an anonymous function written in braces, e.g. `{ it > 0 }`. A **higher-order function** is a function that takes another function as a parameter — `filter` takes a *predicate* (a function returning `Boolean`). The **trailing-lambda convention** says: when the **last** parameter of a function is a function type, you may write the corresponding lambda argument **outside** the parentheses. ```kotlin val positives = list.filter({ it > 0 }) // canonical call val positives = list.filter() { it > 0 } // lambda moved out, () stays val positives = list.filter { it > 0 } // empty () dropped — idiomatic ``` All three are the same call. The first form is rarely written by hand; idiomatic Kotlin uses the third. ## With other arguments Only the **last** argument moves out. Earlier arguments stay inside the parentheses: ```kotlin val sum = list.fold(0) { acc, x -> acc + x } // 0 inside, lambda outside val s = buildString(64) { append("hi") } // capacity inside, lambda outside ``` ## Why it exists It makes higher-order calls and DSLs read like built-in control flow. `repeat(3) { ... }` looks like a loop; `apply { ... }` looks like a block. Without it every such call would carry visually noisy `({ ... })`. ## The `it` shorthand When a lambda has exactly one parameter you can omit the parameter list and use the implicit name `it`. That is a separate feature, but it commonly appears together with trailing lambdas (`filter { it > 0 }`).

  • If a function takes two lambdas, can both go outside the parentheses?
    No. Only the single last lambda can move out. The earlier lambda must stay inside the parentheses as a normal argument.

Like taking the bulkiest item out of a packed box and carrying it separately so the box closes neatly.

saying these in an interview costs you the question

  • Thinks trailing lambdas change runtime behavior rather than being syntax sugar
  • Believes any argument (not just the last) can be moved out
  • Confuses the trailing-lambda rule with the implicit `it` rule
  • Cannot rewrite filter({...}) into the idiomatic form

context

open as a page

When can you drop the empty `()` entirely, as in `repeat(3) { }` vs `run { }`? Explain the rule precisely.

level: middleimportance: must knowfreq 65%

basics

~20 s

You drop the parentheses only when the lambda is the function's single argument and nothing else is left inside them. run takes only a lambda, so run { }. repeat(3) { } still needs (3) because 3 is a separate argument.

open as a page

A function takes two function-type parameters. How do you call it, and what is the idiomatic way to pass both lambdas cleanly?

level: middleimportance: should knowfreq 45%

basics

~10 s

Only the last lambda can sit outside the parentheses. The earlier lambda stays inside, usually passed by name with the named-argument syntax so the call reads clearly.

open as a page

As an API designer, why does the position of a function-type parameter matter, and how do you order parameters to give callers clean trailing-lambda call sites?

level: seniorimportance: should knowfreq 25%

basics

~10 s

Only the last parameter can be a trailing lambda, so you put the main callback last. That way callers write a clean block after the parentheses instead of a noisy nested lambda.

open as a page

How does the trailing-lambda syntax interact with overload resolution and SAM conversion? When can it cause an ambiguity or surprising call?

level: seniorimportance: should knowfreq 30%

basics

~20 s

The trailing lambda is just normal sugar, so the compiler still matches it to a function-type parameter. Problems appear when two overloads both accept a final function-type argument — then the call can be ambiguous.

open as a page