Scope Functions: let, run, with, apply, also
Prerequisites: Extension functions, Lambdas and higher-order functions
Kotlin's standard library has five small functions that run a block of
code on an object. They differ in two ways: how the block refers to
the object (it or this), and what the function returns.
| Function | Object is | Returns | Typical use |
|---|---|---|---|
let |
it |
the block result | Run code on a non-null value; transform |
run |
this |
the block result | Compute a result from an object |
with |
this |
the block result | Call several methods on one object |
apply |
this |
the object | Configure an object |
also |
it |
the object | Side effects, such as logging |
Examples
class Request {
var url = ""
var method = "GET"
val headers = mutableMapOf<String, String>()
}
fun main() {
// let: only when not null, and transform the value
val email: String? = "Ada@Example.com"
val domain = email?.let { it.substringAfter("@").lowercase() }
println(domain)
// apply: configure a new object, get the object back
val request = Request().apply {
url = "https://api.example.com/users"
method = "POST"
headers["Accept"] = "application/json"
}
println("${request.method} ${request.url}")
// also: do something extra, keep the value flowing
val numbers = mutableListOf(3, 1, 2)
.also { println("Before sorting: $it") }
numbers.sort()
// with: several calls on one object, return a result
val summary = with(numbers) {
"size=$size, first=${first()}, last=${last()}"
}
println(summary)
// run: compute a result from an object
val total = request.run { headers.size + if (method == "POST") 1 else 0 }
println(total)
}
example.com
POST https://api.example.com/users
Before sorting: [3, 1, 2]
size=3, first=1, last=3
2
Choosing
You'll see ?.let { } and apply { } most. Pick based on intent:
- Configuring an object you just created →
apply - Doing something with a value only if it isn't null →
?.let - Logging or checking a value in the middle of a chain →
also
Common mistakes
- Nesting scope functions several deep. When
itandthisstart referring to different objects on nearby lines, plain local variables are clearer. - Using
?.letjust to avoid anif.if (x != null)is often easier to read, and smart casts makexnon-null inside it anyway.