A Kotlin Lambda Is a Value You Can Pass Around
You’ve passed values around since your first program — numbers, strings, objects go into variables and out of functions all day. What you usually couldn’t pass around easily was behavior: a piece of code to run later. In Java that meant ceremony — an anonymous Comparator, a Runnable, an interface with one method wrapped in new. Kotlin removes the ceremony by letting a chunk of code be a value as ordinary as the number 42. That value is called a lambda, and this series builds up to a complete picture of them. We’ll start slow.
This is part one of five, and it follows collections. Here we only answer one question: what is a lambda?
The smallest lambda
A lambda is code wrapped in curly braces:
{ println("Hello") }
That’s a complete lambda. It’s a value — a little package of “print Hello” that hasn’t happened yet. Like any value, you can put it in a variable:
val greet = { println("Hello") }
Here’s the one idea to hold onto from the start: defining a lambda and running it are two different things. The line above defines greet but prints nothing. To actually run the code inside, you call it, with parentheses, like a function:
greet() // now it prints: Hello
greet() // prints again: Hello
Define once, call as many times as you like. If that distinction feels clear, the rest is detail.
Giving it an input
Most code needs something to work on. A lambda can take parameters — you list them at the start, followed by an arrow ->, and then the body:
val greet = { name: String -> println("Hello, $name") }
greet("Ada") // Hello, Ada
Read the braces as two halves split by the arrow: before -> are the inputs, after -> is the body. Everything to the right runs when you call it, with name standing in for whatever you passed.
Giving it an output
A lambda can also hand a value back. You don’t write return — the last expression in the body is automatically the result:
val square = { x: Int -> x * x }
square(3) // 9
x * x is the last (and only) line, so that’s what square(3) produces. The same rule holds whatever the body returns — a string, a boolean, anything:
val shout = { word: String -> word.uppercase() + "!" }
shout("hi") // "HI!"
val isAdult = { age: Int -> age >= 18 }
isAdult(20) // true
And since the result is just “the last expression,” that expression can itself be an if — or any other value-producing piece of Kotlin:
val sign = { n: Int -> if (n >= 0) "positive" else "negative" }
sign(-4) // "negative"
This “last line is the answer” rule is worth remembering; it shows up everywhere in Kotlin.
What is a lambda’s type?
Every value has a type — 42 is an Int, "Ada" is a String. A lambda’s type describes its inputs and output, written with an arrow:
(Int) -> Int
Read that left to right: “takes an Int, gives back an Int.” That’s exactly the type of square. You can write it out explicitly on the left — and when you do, you no longer repeat the parameter’s type inside the braces, since the compiler already knows it:
val square: (Int) -> Int = { x -> x * x }
The other lambdas from a moment ago each have a type you read the same way:
val shout: (String) -> String = { w -> w.uppercase() + "!" } // String in, String out
val isAdult: (Int) -> Boolean = { age -> age >= 18 } // Int in, Boolean out
val log: (String) -> Unit = { msg -> println(msg) } // String in, nothing out
The shape is always (inputs) -> output. A few more, just to practice reading them aloud:
(String, String) -> String // takes two Strings, returns a String
() -> Unit // takes nothing, returns nothing meaningful
(Int) -> Boolean // takes an Int, returns true/false
Unit is Kotlin’s “no meaningful value” — the return type of our greet, which only prints.
it: a shortcut for one parameter
Naming a single parameter often feels like overkill. When a lambda has exactly one parameter, Kotlin lets you skip the name and the arrow and refer to it as it:
val square: (Int) -> Int = { it * it }
it is just the implicit name for that lone parameter. It’s a convenience, not a new concept — and it’s everywhere in real Kotlin, so it’s worth getting comfortable with now. (With two or more parameters, you must name them; there’s no it.)
When one line isn’t enough
A lambda body can span several lines. The rule doesn’t change: the last line is the result, and the lines above it are setup.
val describe = { score: Int ->
val grade = if (score >= 50) "pass" else "fail"
"Result: $grade" // this last line is what comes back
}
describe(72) // "Result: pass"
Final thoughts
That’s the whole foundation, and it really is this small: a lambda is a value that happens to be code — written in braces, with inputs before the -> and a result on its last line, carrying a type like (Int) -> Int. You can store it in a variable and call it whenever you want.
So far we’ve only ever called our lambdas ourselves, which isn’t very useful yet. The power comes from handing a lambda to another function and letting it do the calling. Next: lambdas at work, where this idea transforms how you work with lists.
Practice: reinforce this with the companion workbook — short, click-to-reveal problems.
Comments