As a tech lead, what guidelines would you give for choosing an anonymous function over a lambda, and what are the maintainability and performance implications?
answer
- lambda by default, anon fun for reasons
- anon fun: local return, explicit type/receiver, disambiguate
- no trailing-lambda for anon fun
- same FunctionN lowering -> equal performance
- inline -> no allocation either way
basics
~20 sPrefer concise lambdas by default. Use an anonymous function when you need a plain local return without a label, an explicit return type, a stated receiver, or to resolve overload ambiguity. Both compile to the same function types, so there is no inherent performance gap.
solid answer
~50 sDefault to lambdas — they are shorter, support trailing-lambda syntax and implicit it, and are idiomatic. Choose an anonymous function for specific reasons: (1) you want a bare return that exits only that block without a return@label, improving readability of early-exit logic; (2) you need an explicit, declared return type for clarity or generic resolution; (3) you must disambiguate overloaded functional parameters; (4) you want to state a receiver at the literal. Maintainability-wise, anonymous functions read as 'a real function' and avoid surprising non-local returns, but they cannot use trailing-lambda call syntax (the literal can't sit outside the parentheses) and lose implicit it, making call sites noisier. Performance is identical: both lower to the same FunctionN / SAM constructs, and both are inlined when passed to inline functions. So the decision is about clarity and correctness of return semantics, never speed.
go deeper
Can say lambdas are the usual choice but lacks the criteria for when to switch.
Lists a couple of valid reasons (local return, explicit type) without the performance or DSL nuance.
Gives a complete decision framework and correctly states performance equivalence.
Sets team conventions, weighs readability/DSL tradeoffs, and ties the choice to refactor-safety and API design.
## Decision framework **Default: lambda.** It is the idiomatic, concise choice and enables trailing-lambda syntax, implicit `it`, and clean DSLs. **Switch to an anonymous function when one of these applies:** 1. **Local return without a label.** A plain `return` inside an anonymous function exits only that block. When early-exit logic would otherwise need `return@forEach`/`return@run`, an anonymous function can read more clearly and removes the risk of an accidental non-local return. 2. **Explicit return type.** `fun(x: Int): BigDecimal { ... }` documents intent and can resolve generic inference where a lambda's inferred type is ambiguous. 3. **Overload disambiguation.** Explicit parameter types pin exactly one overloaded functional parameter (see the senior question on this leaf). 4. **Explicit receiver.** `fun Foo.(p): R { ... }` states the receiver at the literal. ## Maintainability tradeoffs - **Pro:** anonymous functions make return semantics obvious and behave like ordinary functions, which reduces non-local-return surprises during refactors. - **Con:** no trailing-lambda syntax — the literal must stay inside the parentheses, so `list.forEach(fun(x: Int) { ... })` is wordier than `list.forEach { ... }`. You also lose implicit `it` and must name parameters. - **Consistency:** mixing both forms across a file can be jarring; teams usually standardize on lambdas and use anonymous functions only for the four reasons above. ## Performance: no difference Both forms lower to the same representation. When the receiving function is `inline`, the literal's body is inlined and **no `Function` object is allocated** in either case. When it is not inline, both allocate an equivalent `FunctionN` / lambda class (or use a SAM conversion for Java interop). There is no allocation, dispatch, or inlining advantage to either form. Therefore the choice is purely about **readability and return-control correctness**, not throughput. ```kotlin // Idiomatic default items.firstOrNull { it.isActive } // Justified anonymous function: explicit local return reads clearly val validate = fun(input: String): Result<Unit> { if (input.isBlank()) return Result.failure(IllegalArgumentException("blank")) if (input.length > 100) return Result.failure(IllegalArgumentException("too long")) return Result.success(Unit) } ``` ## What to tell the team - Lambdas by default; anonymous functions for return clarity, explicit types, overload disambiguation, or explicit receivers. - Never choose between them for performance — they are equivalent. - Watch the block-body `Unit` trap when converting a lambda to an anonymous function.
- Does using an anonymous function ever cost more at runtime than a lambda?No. Both lower to equivalent function objects, and both are inlined when passed to inline functions; there is no allocation or dispatch difference.
- Why can't an anonymous function use trailing-lambda call syntax?Trailing-lambda syntax only applies to lambda literals placed outside the parentheses; an anonymous function literal must be an ordinary argument inside them.
Lambdas are shorthand notes; anonymous functions are the formal memo — same content, you pick the register that prevents misreading.
saying these in an interview costs you the question
- Claiming anonymous functions are faster or slower than lambdas
- Recommending anonymous functions as the default style
- Forgetting they lose trailing-lambda syntax and implicit it
- Ignoring the return-semantics rationale, the main legitimate reason
- Not mentioning the block-body Unit trap when advising conversions