Skip to content
Lesson 10 of 19

Step 1 of 5 · Reading · ~4 min

Learn

Functions and Lambdas

Lambdas and Higher-Order Functions

A lambda is a function with no name, written between braces, that you can store in a variable and hand to other functions.

fun main() {
    val double: (Int) -> Int = { n -> n * 2 }
    val join: (String, String) -> String = { a, b -> a + " " + b }

    println(double(5))
    println(join("ship", "code"))
}

//> 10
//> ship code

There are two halves to read here. (Int) -> Int is a type — "takes an Int, gives back an Int" — and { n -> n * 2 } is a value of that type. Parameters go to the left of the arrow, the body to the right.

No return inside a lambda

A lambda's result is simply its last expression. No return keyword is involved.

✓ Correct — the last line is the result:

val classify: (Int) -> String = { n ->
    val sign = if (n < 0) "neg" else "pos"
    sign
}

✗ Wrong — return inside a lambda tries to return from the enclosing function, which here is a compile error and in general is never what a beginner means:

val classify: (Int) -> String = { n -> return if (n < 0) "neg" else "pos" }

it: the name you get for free

When a lambda takes exactly one parameter you may skip naming it. Kotlin calls it it.

fun main() {
    val nums = listOf(1, 2, 3, 4)

    println(nums.map { it * 2 })
    println(nums.filter { it % 2 == 0 })
}

//> [2, 4, 6, 8]
//> [2, 4]

Note how the list is built: listOf(1, 2, 3, 4). Kotlin has no [1, 2, 3] literal syntax, so square brackets there would not compile. Use it for short one-liners and give the parameter a real name the moment the lambda grows past one line or sits inside another lambda.

Higher-order functions

A function that accepts or returns another function is called higher-order. Declare the parameter with a function type, then call it like anything else.

fun applyTwice(n: Int, op: (Int) -> Int): Int {
    return op(op(n))
}

fun main() {
    println(applyTwice(3) { it + 1 })
    println(applyTwice(3) { it * it })
}

//> 5
//> 81

Follow the second one through: the inner op(3) gives 9, and the outer op(9) gives 81. applyTwice never knows what op does — that is exactly the point.

The trailing-lambda rule

If the last parameter is a function, the lambda may move outside the parentheses. If it is the only argument, the parentheses vanish entirely.

Written asLegalIdiomatic
applyTwice(3, { it * it })yesno
applyTwice(3) { it * it }yesyes
nums.map({ it * 2 })yesno
nums.map { it * 2 }yesyes

That single rule is why nums.map { it * 2 } reads like built-in syntax when it is really an ordinary function call. The whole standard library — map, filter, forEach, sortedBy, fold — is built on it.

fun main() {
    println(listOf(1, 2, 3, 4).fold(0) { acc, n -> acc + n })
}

//> 10

fold starts from 0 and folds each element in with your lambda: 0 + 1, then 1 + 2, then 3 + 3, then 6 + 4.

Your exercise

Apply Twice asks you to finish applyTwice(n, op) so it applies op to n and then applies op to that result. The main you are given calls it with a squaring lambda, so the printed answer is N to the fourth power.

Two mistakes the grader catches. First, the starter's body is return 0; leave it sitting below your own line and every test prints 0, because the first return reached wins. Second, applying op only once — return op(n) — prints 4 for the visible test that feeds 2 and expects 16. The calls have to nest: the result of the inner call is the argument to the outer one.

Up nextExtension FunctionsFunctions and Lambdas

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…