skip to content

Lambda with Receiver A.()->Unit

A parameter typed A.() -> Unit means the block runs with an A as this, so its members can be called without qualification. Recognizing this type in a signature is how you read any builder API.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What is a function type with receiver like A.() -> Unit, and how does it differ from a plain (A) -> Unit?

level: juniorimportance: must knowfreq 70%

answer

  1. Receiver becomes this, members unqualified
  2. Call as a.f() OR f(a)
  3. (A)->Unit uses it; A.()->Unit uses this
  4. apply/with use receivers; let/also use it
  5. Foundation of builder DSLs

basics

~10 s

It is a lambda that runs as if it lives inside an object of type A. Inside the block, you can call A's methods directly without naming the object, because A becomes 'this'.

solid answer

~40 s

A.() -> Unit is a function type with receiver: when you invoke such a lambda you must supply an instance of A, and inside the body that instance becomes the implicit receiver this. So members of A can be called unqualified. A plain (A) -> Unit takes A as an ordinary parameter you reference by name (e.g. it). Syntactically: with `val f: A.() -> Unit`, you call it as `a.f()` or `f(a)`, and the body sees `this == a`. With `val g: (A) -> Unit` you call `g(a)` and refer to the value as `it`. Receiver types power builder DSLs (e.g. `html { body { ... } }`) and stdlib functions like apply and with. The lambda is compiled to a function whose first 'extension' parameter is the receiver.

code

kotlin · 10 lines
kotlin
fun StringBuilder.demo(block: StringBuilder.() -> Unit) = block()

val s = StringBuilder().apply {       // apply: T.() -> Unit
    append("Hello")                   // this.append
    append(", world")
}
println(s)                            // Hello, world

val parameterStyle: (StringBuilder) -> Unit = { sb -> sb.append("!") }
val receiverStyle: StringBuilder.() -> Unit = { append("!") }

go deeper

for a junior

Knows the body runs with A as this and members are called unqualified.

for a middle

Distinguishes this vs it, knows apply/with vs let/also, and both call forms.

for a senior

Explains the extension-receiver compilation and why DSLs rely on it.

for a principal

Frames receiver lambdas as the language primitive behind type-safe builders and discusses API design trade-offs of receiver vs parameter lambdas.

## What it is A **function type with receiver** has the shape `Receiver.() -> ReturnType`. The part before `.()` is the **receiver type**. When you call a value of this type, you provide an instance of the receiver, and inside the lambda body that instance becomes the **implicit receiver** — referenced as `this` and usable to call members **unqualified**. ```kotlin val greet: String.() -> Unit = { println("len=$length, upper=${uppercase()}") } // length and uppercase() are String members, called with no qualifier "hi".greet() // call style 1: receiver before the dot greet("hi") // call style 2: receiver as first argument ``` Both call styles are legal for a receiver type. Inside the body, `this` is the `String`, so `length` means `this.length`. ## Versus a plain parameter lambda - `(A) -> Unit` — A is an **ordinary parameter**. You access it by name, or as the implicit single-parameter name `it`. There is no implicit `this` of type A. - `A.() -> Unit` — A is the **receiver**. It becomes `this`; there is no `it` for it. Members are called unqualified. ```kotlin val p: (StringBuilder) -> Unit = { it.append("x") } // must write it. val r: StringBuilder.() -> Unit = { append("x") } // append() is this.append() ``` ## Why it exists Receiver lambdas are the foundation of **type-safe builder DSLs**. A function like `fun html(block: HTML.() -> Unit)` lets callers write `html { head { ... }; body { ... } }`, calling `head`/`body` (members of `HTML`) directly inside `{ }`. The stdlib scope functions `apply` and `with` use receiver lambdas (`T.() -> Unit`, `T.() -> R`), while `let` and `also` use plain `(T) -> R` parameter lambdas (so they use `it`). ## Under the hood The compiler turns `A.() -> Unit` into a function value whose receiver is passed like an **extension-function receiver** (effectively a hidden first argument). That is why the two call forms `a.f()` and `f(a)` are interchangeable, and why an extension function reference `A::member` (and an extension lambda) can satisfy this type. ## Key keywords/APIs - `this` — the implicit receiver inside the body. - `apply` (`T.() -> Unit`), `with` (`T.() -> R`) — receiver lambdas. - `let` / `also` — plain `(T) -> R` lambdas, use `it`. - Extension function declarations and references compose with these types.

  • Which stdlib scope functions use a receiver lambda and which use a parameter lambda?
    apply and with use receiver lambdas (this); let and also use parameter lambdas (it); run has a receiver-lambda form.
  • Can you call a receiver lambda f: A.() -> Unit using the syntax f(a)?
    Yes. Both a.f() and f(a) are valid; the receiver may be passed as the first argument.

Plain lambda hands you a tool you must name; a receiver lambda puts you inside the workshop so every tool is just within reach.

saying these in an interview costs you the question

  • Says the receiver is accessed via 'it' inside the body
  • Claims A.() -> Unit and (A) -> Unit are interchangeable types
  • Thinks you cannot call members unqualified inside the block
  • Confuses apply (receiver) with let (parameter)

context

open as a page

Show how fun html(block: HTML.() -> Unit) lets callers write html { body { ... } } with unqualified member calls. Why does the unqualified call work?

level: middleimportance: must knowfreq 65%

basics

~20 s

The function gives the lambda an HTML object as its hidden 'this'. So inside the braces you can call HTML's methods like body() directly, with no object name in front. That is how the builder reads like English.

open as a page

Implement apply and with yourself using receiver lambdas. What are their exact signatures, and how do they differ from let?

level: middleimportance: should knowfreq 60%

basics

~20 s

apply runs a block on an object and returns the object; with does the same but returns the block's result. Both make the object 'this'. let differs: it passes the object as 'it' and returns the result.

open as a page

Given fun build(block: StringBuilder.() -> Unit), what kinds of values can you pass as block? Can a regular extension function or a (StringBuilder) -> Unit lambda be passed?

level: seniorimportance: should knowfreq 40%

basics

~20 s

You can pass a lambda written with the receiver style, or a reference to an extension function on StringBuilder. A plain (StringBuilder) -> Unit lambda is a different type, so it needs adapting, though references convert in many cases.

open as a page

Inside a receiver lambda, what does this refer to, and how do you reach an outer receiver or the lambda's own label? Explain this@Outer and the difference from named-parameter access.

level: seniorimportance: should knowfreq 35%

basics

~20 s

Inside the block, this is the receiver the lambda was invoked on. When nested inside another receiver block, you reach the outer one with a label, like this@Outer. A plain parameter is reached by its name or it instead.

open as a page