Single-expression functions
Many functions do exactly one thing: compute a value and return it. Kotlin has a shorter syntax for these — the single-expression function — and using it well makes code cleaner. This short lesson covers the form and, just as importantly, when not to use it.
The form
When a function's body is a single expression, you can drop the braces and the return, and use =
instead:
// block body
fun square(n: Int): Int {
return n * n
}
// single-expression body — the same function
fun square(n: Int): Int = n * n
Read fun square(n: Int): Int = n * n as "square of n is n times n". The = says the function
equals the expression on the right. No braces, no return — the expression's value is the return
value. It is a small saving on one function and a large saving on readability across a file full of
them.
Type inference for the return type
With a single-expression function, Kotlin can infer the return type from the expression, so you may omit it:
fun square(n: Int) = n * n // return type Int, inferred
fun greet(name: String) = "Namaste, $name" // return type String, inferred
fun isAdult(age: Int) = age >= 18 // return type Boolean, inferred
This is the one place idiomatic Kotlin routinely omits the return type. It is fine for short, obvious functions where the type is clear from the expression. But for a public function, or one whose return type is not instantly obvious, write the type anyway — it documents the contract and stops an accidental change to the body from silently changing the return type. A private one-liner can infer; a function others call should state its type.
Where it shines
Single-expression functions are perfect for:
// simple computations
fun areaOfCircle(r: Double) = 3.14159 * r * r
// delegating to something else
fun fullName(first: String, last: String) = "$first $last"
// wrapping a when expression (very common and very readable)
fun grade(score: Int): String = when {
score >= 90 -> "A"
score >= 70 -> "B"
score >= 50 -> "C"
else -> "F"
}
// wrapping an if expression
fun max(a: Int, b: Int) = if (a > b) a else b
The when and if cases are worth noticing: because those are expressions (they return values),
they slot straight into a single-expression function. fun grade(...) = when { ... } is a common,
clean pattern — a function whose whole job is to map an input to one of several outputs, written as
one expression. You will use this shape constantly.
When not to use it
The single-expression form is a tool, not a mandate. Do not contort a function into one expression just to use it:
When the function does more than one thing. If it validates, then computes, then logs, it is not a single expression — it is a block body, and forcing it into one line with clever operators makes it harder to read, not easier. Use braces and let it breathe.
When the single expression is long or deeply nested. A single-expression function whose one
expression is a three-line chain of nested ?.let { } and ?: is technically one expression and
practically unreadable. If the reader has to work to parse it, a block body with intermediate vals
and clear names is better.
When you want intermediate values with names. Sometimes naming a step aids the reader:
// clearer as a block, despite being "computable" as one expression
fun priceWithTax(base: Int): Int {
val tax = base / 10
val rounded = ((base + tax) / 100) * 100
return rounded
}
The val tax and val rounded document what each step is. Squashing this into one expression would
save two lines and cost clarity — a bad trade.
The rule
Use a single-expression function when the body genuinely is one clear expression; use a block body when it is not. The goal is always readability, never brevity for its own sake. A file where the simple functions are one-liners and the complex ones are clear blocks reads beautifully; a file where everything is crammed into one expression reads like a puzzle.
Check your work
The single-expression form. fun name(params): Type = expression — no braces, no return.
How to read fun square(n: Int) = n * n. "The square of n equals n times n" — the expression is
the return value.
When you can omit the return type. For a single-expression function, Kotlin infers it — fine for short private functions.
When to write the return type anyway. For a public function, or when the type is not obvious — it documents the contract.
Why when and if fit single-expression functions. They are expressions that return values, so
they slot straight in.
Three cases where you should not use the form. When the function does more than one thing; when the expression is long or nested; when named intermediate values aid the reader.
The rule. One clear expression → single-expression; otherwise a block body. Readability over brevity.
Practice
- Rewrite three block-body functions (
square,greet,isAdult) as single-expression functions. - Write
grade(score: Int): String = when { ... }and confirm it works for several scores. - Write
max(a, b) = if (a > b) a else band test it. - Omit the return type on a one-liner and confirm (in the IDE) the inferred type is right. Then add the type back for a public-facing one.
- Take a function that validates and computes and try to force it into one expression. Decide whether the result is more or less readable, and revert if worse.
- Find a function you have written that is genuinely one expression and shorten it to the single-expression form.
Official documentation
- Kotlin — Functions: single-expression functions — The form and when the return type can be omitted.
- Kotlin — Coding conventions: functions — Guidance on expression bodies and formatting.
Next: extension functions, one of Kotlin's most distinctive features.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship