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