skip to content

When does an anonymous function's block body return Unit unexpectedly, and how do explicit vs inferred return types behave for block versus expression bodies?

level: middleimportance: should knowfreq 35%

answer

  1. block body default = Unit
  2. expression body = inferred type
  3. anon fun block != lambda last-expr
  4. need explicit return in block body
  5. fun(x)=x*2 is the safe rewrite

basics

~20 s

An anonymous function with a block body { } returns Unit unless you declare a return type or use a return statement. An expression body (= ...) infers its type from the expression. So a block body without an explicit type can surprise you by returning Unit.

solid answer

~50 s

For an **expression-body** anonymous function (fun(x: Int) = x * 2) the return type is **inferred** from the expression, exactly like a regular expression-body function. For a **block-body** anonymous function (fun(x: Int) { ... }) the return type is **Unit** unless you declare it explicitly (fun(x: Int): Int { ... }) and actually return a value. Unlike a lambda — whose result is the value of its last expression — a block-bodied anonymous function does NOT implicitly return its last expression; you must use return. This mirrors normal fun declarations and is a common source of confusion when converting a lambda { it * 2 } to fun(it: Int) { it * 2 }, which silently becomes () -> Unit. The fix is either an expression body fun(x: Int) = x * 2 or an explicit return: fun(x: Int): Int { return x * 2 }.

go deeper

for a junior

Knows expression body infers a type; may not anticipate the block-body Unit trap.

for a middle

Explains the Unit default for block bodies and contrasts it with lambda last-expression semantics.

for a senior

Predicts where a silent Unit will compile vs error and refactors lambdas to anonymous functions safely.

for a principal

Anticipates these traps in API design and code review, steering toward forms that fail loudly.

## Two body forms, two rules Anonymous functions follow the **same return-type rules as named functions**, which differ from lambdas. ### Expression body — type is inferred ```kotlin val f = fun(x: Int) = x * 2 // inferred return type Int -> type is (Int) -> Int ``` The `= expression` form infers the return type from the expression. You may still annotate it: `fun(x: Int): Int = x * 2`. ### Block body — defaults to Unit ```kotlin val g = fun(x: Int) { x * 2 } // block body, NO return -> return type Unit -> (Int) -> Unit ``` A block body `{ ... }` does **not** treat its last expression as the result. Without an explicit `return`, the function returns `Unit`. `x * 2` here is a discarded expression. To return a value you must: ```kotlin val h = fun(x: Int): Int { return x * 2 } // (Int) -> Int ``` ## Contrast with lambdas A lambda's value **is** its last expression: ```kotlin val lam: (Int) -> Int = { x -> x * 2 } // last expression x*2 is the result ``` So the dangerous refactor is mechanically turning a lambda into a block-body anonymous function: ```kotlin // before list.map { it * 2 } // WRONG: becomes (Int) -> Unit, won't satisfy map's (Int) -> R where R = Unit -> List<Unit> list.map(fun(it: Int) { it * 2 }) // RIGHT list.map(fun(it: Int) = it * 2) list.map(fun(it: Int): Int { return it * 2 }) ``` ## Why this matters The compiler will usually catch the mismatch when the expected type requires a non-Unit result, producing a type error. But in positions that accept `(T) -> Unit` (e.g. `forEach`), the silent `Unit` result compiles fine and your computed value is quietly thrown away. ## Recall - Expression body `= e` -> inferred return type. - Block body `{ }` -> `Unit` unless you `return` a value (and usually declare the type). - Lambda -> last expression is the result; anonymous function block body -> it is not.

  • Why does fun(it: Int) { it * 2 } compile but produce the wrong type?
    Block bodies return Unit unless you return a value; it * 2 is evaluated and discarded, so the type is (Int) -> Unit.
  • Does declaring : Int on a block body without a return statement compile?
    No. If you declare a non-Unit return type you must return a value on all paths, or the compiler errors that a return is expected.

saying these in an interview costs you the question

  • Claiming a block-body anonymous function returns its last expression like a lambda
  • Thinking expression and block bodies follow the same return rule
  • Not realizing a silent Unit can compile in forEach-style positions
  • Believing the return type is always required to be declared

context