Receiver lambdas expose a controlled vocabulary
A Builder.() -> Unit lambda makes builder members directly available inside a block. The caller still receives an ordinary function value; no special parser or runtime language is involved.
class Topics {
private val items = mutableListOf<String>()
fun add(title: String) { require(title.isNotBlank()); items.add(title) }
fun snapshot(): List<String> = items.toList()
}
fun topics(block: Topics.() -> Unit): List<String> = Topics().apply(block).snapshot()
fun main() { check(topics { add("Kotlin") } == listOf("Kotlin")) }
The receiver limits the obvious operations offered by completion. Avoid exposing internal mutable collections, or callers can bypass validation and retain references after construction.
Exercise
Add a second operation that normalizes whitespace before adding a title, then compare whether a single consistently validating operation is clearer. Rewrite the block as an ordinary lambda taking a named builder parameter.
Check: receiver syntax should improve readability without changing the validation rules or copying behavior.