skip to content

How do you write a function that RETURNS a function in Kotlin, and what is captured when the returned lambda references the enclosing function's parameters?

level: middleimportance: should knowfreq 65%

answer

  1. Return type is a function type: (Int) -> Int
  2. Returned lambda = closure over enclosing scope
  3. Each call => fresh captured binding
  4. Kotlin can capture+mutate var (Ref box)
  5. Basis for partial application / factories

basics

~10 s

Give the function a function-type return, like (Int) -> Int, and return a lambda. The lambda remembers (captures) the outer variables it uses, so each call can make a customized function.

solid answer

~40 s

Declare the return type as a function type, e.g. fun adder(base: Int): (Int) -> Int = { x -> x + base }. The returned lambda is a closure: it captures the variable base by reference to the surrounding scope, keeping it alive after adder returns. Each invocation of adder produces a distinct function value bound to its own base. Unlike Java, Kotlin closures can capture and even mutate var locals (the compiler boxes a captured var in a Ref wrapper). You return the lambda directly, a function reference, or a stored value of the right type. Callers store it (val add5 = adder(5)) and invoke later: add5(10) == 15. This underlies currying/partial-application idioms and factory-style APIs.

code

kotlin · 8 lines
kotlin
fun multiplier(factor: Int): (Int) -> Int = { it * factor }

fun main() {
    val double = multiplier(2)
    val triple = multiplier(3)
    println(double(10)) // 20
    println(triple(10)) // 30 - independent captured factor
}

go deeper

for a junior

Can write a factory that returns a lambda and call the result later.

for a middle

Explains closures and per-call capture, and the val/var capture difference vs Java.

for a senior

Describes the Ref-boxing mechanism for mutable capture and memory/lifetime implications.

for a principal

Weighs closure allocation cost, captured-state lifetimes/leaks, and when to prefer classes or inline.

## Returning a function To return a function, make the **return type** a function type and produce a value of that type: ```kotlin fun adder(base: Int): (Int) -> Int { return { x -> x + base } } // expression-body form: fun adder(base: Int): (Int) -> Int = { x -> x + base } ``` `adder(5)` does not add anything yet — it returns a **new function value** that, when later called, adds 5. ## Closures and capture The returned lambda `{ x -> x + base }` references `base`, a parameter of the *enclosing* `adder`. The lambda **captures** `base` — it closes over the enclosing scope. This is called a **closure**. The captured binding stays alive even after `adder` has returned: ```kotlin val add5 = adder(5) // base=5 captured val add10 = adder(10) // separate base=10 captured add5(1) // 6 add10(1) // 11 ``` Each call to `adder` yields an independent function value with its own captured `base`. ## Capturing `val` vs `var` - A captured **`val`** is read-only; the lambda just reads it. - Unlike Java (which only allows *effectively final* captures), Kotlin lets a lambda capture and **mutate a `var`**. The compiler implements this by boxing the variable in a hidden `Ref` object (e.g. `Ref.IntRef`) shared between the outer scope and the lambda: ```kotlin fun counter(): () -> Int { var n = 0 return { n++; n } // mutates captured var via a Ref box } val c = counter(); c(); c(); c() // 1, 2, 3 ``` ## What can you return? Anything assignable to the declared function type: - a lambda `{ ... }`, - a function reference `::someTopLevelFun`, - a stored function value passed through. ## Why it matters Returning functions is the foundation of **partial application / currying** idioms and **factory** functions that pre-bake configuration, producing reusable specialized callables.

  • How does Kotlin let a lambda mutate a captured var when Java cannot?
    The compiler boxes the var in a shared Ref wrapper (e.g. Ref.IntRef); both the enclosing frame and the lambda read/write the same box.
  • Do two calls to adder share the same captured base?
    No. Each invocation creates a new closure with its own captured copy of the parameter binding.

adder(5) is a vending machine pre-loaded with the number 5; press it later with any input and it dispenses input+5.

saying these in an interview costs you the question

  • Saying the returned lambda runs immediately instead of being a deferred value
  • Claiming Kotlin cannot capture mutable variables (confusing it with Java)
  • Thinking all closures from one factory share mutable state
  • Forgetting to declare the function-type return and returning a plain value

context