A function takes two function-type parameters. How do you call it, and what is the idiomatic way to pass both lambdas cleanly?
answer
- Only the LAST lambda trails
- Earlier lambda → named argument inside ()
- Two trailing lambdas = compile error
- Receiver-lambda DSL collapses many callbacks into one block
- Put the primary callback last
basics
~10 sOnly 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.
solid answer
~40 sThe trailing-lambda convention applies to the **last** parameter only. If a function has two lambda parameters, you can move out just the final one; the earlier one must be a normal argument inside the parentheses. The clean approach is to pass the earlier lambda as a **named argument** (`onError = { ... }`) so the call self-documents, then let the final one trail: `fetch(onError = { log(it) }) { data -> render(data) }`. Many APIs deliberately order parameters so the most-used callback is last. If two trailing lambdas would genuinely help, that is a signal to redesign — split into a builder/DSL, or use a single receiver lambda that exposes both callbacks (the pattern Kotlin coroutines and many DSLs use). Forcing both lambdas inside `(f1, f2)` is legal but reads poorly.
code
kotlin · 9 linesfun request(
onError: (String) -> Unit,
onSuccess: (String) -> Unit,
) { /* ... */ }
// idiomatic: name the first, trail the second
request(onError = { msg -> println("err: $msg") }) { body ->
println("ok: $body")
}go deeper
Knows only one lambda trails and the other goes inside the parentheses.
Uses a named argument for the non-trailing lambda and predicts that two brace blocks won't compile.
Proposes reordering parameters or a receiver-lambda DSL; explains function-literal-with-receiver.
Treats lambda ordering as an API-design contract and weighs DSL vs multi-callback trade-offs.
## Only the last parameter trails The convention is defined for the **final** parameter. Given: ```kotlin fun load( onError: (Throwable) -> Unit, onSuccess: (String) -> Unit, ) ``` you can only trail `onSuccess`: ```kotlin load(onError = { e -> log(e) }) { data -> render(data) } ``` The `onError` lambda stays inside the parentheses. Using a **named argument** (`onError = ...`) keeps the call readable. ## You cannot trail both This does **not** compile: ```kotlin load { e -> log(e) } { data -> render(data) } // ERROR ``` Kotlin has no syntax for two trailing lambdas. ## Idiomatic alternatives When two callbacks are common, prefer one of: - **Order so the primary callback is last**, and pass the secondary by name (shown above). - **A receiver-based DSL**: take a single lambda whose receiver type exposes both hooks. ```kotlin class LoadScope { var onError: (Throwable) -> Unit = {} var onSuccess: (String) -> Unit = {} } fun load(config: LoadScope.() -> Unit) { /* ... */ } load { onError = { log(it) } onSuccess = { render(it) } } ``` Here `LoadScope.() -> Unit` is a **function literal with receiver** — inside the braces `this` is a `LoadScope`, so you set its properties. This is how builder DSLs (and parts of the coroutines API) collapse multiple callbacks into one clean trailing lambda. ## Why this matters for API design Because only one lambda can trail, the **last** function-type parameter is the privileged one. Library authors order parameters so the call site reads best.
- Why can't you write two adjacent `{ }` blocks after the parentheses?Kotlin's grammar allows at most one trailing lambda. A second `{ }` would be parsed as a new statement/lambda, not as a second argument.
- How do coroutine-style or builder DSLs avoid this problem?They take a single function literal with receiver; inside it you assign properties or call DSL methods, so multiple callbacks fit in one trailing block.
saying these in an interview costs you the question
- Believes both lambdas can trail with two brace blocks
- Doesn't reach for named arguments to keep the call readable
- Never mentions reordering parameters or a receiver DSL
- Thinks the limitation is arbitrary rather than a grammar rule