Lambdas with Receivers

The Building Block of DSLs

A lambda with receiver has the type T.() -> R. Inside it, this is a T, so you can call T's members directly. This is how apply, buildString and Gradle's dependencies { } work.

Example

fun buildMessage(action: StringBuilder.() -> Unit): String {
    val sb = StringBuilder()
    sb.action()                 // sb becomes 'this' inside the lambda
    return sb.toString()
}

fun main() {
    val msg = buildMessage {
        append("Hello, ")
        append("Kotlin")
        append("!")
    }
    println(msg)

    // the standard library has the same thing
    val receipt = buildString {
        appendLine("Item: Coffee")
        appendLine("Qty: 2")
        append("Total: Rs 360")
    }
    println(receipt)

    val ids = buildList { add(1); addAll(listOf(2, 3)); add(4) }
    println(ids)
}

Output

Hello, Kotlin!
Item: Coffee
Qty: 2
Total: Rs 360
[1, 2, 3, 4]

Function Type vs Receiver Type

TypeInside the lambdaCall it
(StringBuilder) -> Unitit.append("x")block(sb)
StringBuilder.() -> Unitappend("x") (implicit this)sb.block() or block(sb)