Kotlin
2026-08-21 09:00
Programming with Lambdas taught you to use filter and map.
This unit is about writing them.
In Java, every lambda is an object, and calling it is an indirection.
In a hot loop, that is a real reason to write the imperative version.
inline — and it is more thorough than it sounds.
The compiler substitutes the function’s body and the lambda’s body.
No object. No call. Nothing left of the abstraction.
inline does.use and withLock.The return type is always written, even when it is Unit.
The arrow needs something after it.
Without the outer parentheses, the ? attaches to the return type.
There: a SAM conversion into a Java interface instance.
Here: a proper type. No conversion happens or is needed.
Underneath it compiles to Function1 — so Java can still call it.
The IDE shows those names at the call site.
Real documentation for whoever writes the lambda.
Doing this once changes how the standard library reads.
Default value, usually. The caller sees a working default; the body has no null handling.
Nullable, when absence is genuinely meaningful.
Another convention, from unit 7.
Calling a value with parentheses means calling its invoke.
Which is why ?.invoke() is the safe-call form.
The returned lambda captures the enclosing parameters.
By hand: a chain of ifs inside the filtering loop.
Re-evaluated per element.
Composing a function moves the logic to one place.
Average duration for Windows users.
Then for Mac users.
Then filtered by page.
fun List<SiteVisit>.averageDurationFor(predicate: (SiteVisit) -> Boolean) =
filter(predicate).map(SiteVisit::duration).average()
log.averageDurationFor { it.os == OS.WINDOWS }
log.averageDurationFor { it.os in setOf(OS.ANDROID, OS.IOS) }
log.averageDurationFor { it.os == OS.IOS && it.path == "/signup" }Java’s answer: a strategy interface with an implementation per case.
Several files for what is here one parameter.
And the caller writes the behaviour inline, where a reader can see it.
In Kotlin, “extract the difference” often means extracting a function.
In Java it would mean extracting a type.
No object anywhere.
If a lambda parameter is stored or passed on,
its body cannot be substituted — something is holding it.
noinline exempts that parameter; the rest still inlines.
Small functions taking lambdas — which is why filter costs no more than a loop.
Not large bodies — the body is copied into every call site.
inline removes the lambda overhead.
It does not remove the intermediate collections.
That is what sequences were for. Different costs, different tools.
Acquire, run, release in a finally.
try-with-resources — a language feature, in the compiler.
Kotlin needs none. use is an ordinary extension function.
Because it is inline.
Without that, every use would allocate.
Free higher-order functions are what let a library do a language’s job.
The return inside the use block
returns from readFirstLineFromFile, not from the lambda.
Which is the next section — and only possible because use is inline.
forEach with a return behaves exactly like a for loop with a return.
That is what makes the functional version a genuine replacement.
The lambda’s body has been compiled into the enclosing function.
So the return is an ordinary return from that function.
A non-inlined lambda lives in a separate object. The compiler forbids it.
The lambda’s equivalent of continue.
returnreturns from the nearest enclosing function declared withfun.
In a lambda, that is the outer function.
In an anonymous function, it is the anonymous function.
Lambdas for nearly everything. They read better.
An anonymous function when several returns would each need a label.
use is a function where Java needed syntax.filter once and the standard library stops being magic.Generics — type parameters, constraints, and erasure.
Kotlin’s answer to erasure is reified type parameters.
Which work only because of inlining. One more thing this unit paid for in advance.
Kotlin · Higher-Order Functions: Lambdas as Parameters and Return Values