What is a function type with receiver like A.() -> Unit, and how does it differ from a plain (A) -> Unit?
answer
- Receiver becomes this, members unqualified
- Call as a.f() OR f(a)
- (A)->Unit uses it; A.()->Unit uses this
- apply/with use receivers; let/also use it
- Foundation of builder DSLs
basics
~10 sIt 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 sA.() -> 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 linesfun 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
Knows the body runs with A as this and members are called unqualified.
Distinguishes this vs it, knows apply/with vs let/also, and both call forms.
Explains the extension-receiver compilation and why DSLs rely on it.
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)