Kotlin Scope Functions
Scope functions — let, run, with, apply, and also — execute a block of code in the context of an object. They reduce repeated variable names and make setup and transformation chains more readable.
The Five Scope Functions at a Glance
Function │ Object inside block │ Returns │ Use case
─────────┼─────────────────────┼──────────────┼───────────────────────
let │ it │ block result │ null checks, transform
run │ this │ block result │ compute and return
with │ this │ block result │ operate on an object
apply │ this │ the object │ configure/set up
also │ it │ the object │ side effects, logging
let — Transform and Check Null
val name: String? = " alice "
val result = name?.let {
it.trim().capitalizeWords()
} ?: "Anonymous"
println(result) // Alice
// Without let:
val result2 = if (name != null) name.trim() else "Anonymous"run — Compute and Return
val config = run {
val host = "localhost"
val port = 8080
"$host:$port" // returned as the result
}
println(config) // localhost:8080with — Operate on an Object
data class Report(var title: String = "", var pages: Int = 0, var author: String = "")
val summary = with(Report()) {
title = "Quarterly Review"
pages = 24
author = "Finance Team"
"Report '$title' by $author ($pages pages)" // returned
}
println(summary) // Report 'Quarterly Review' by Finance Team (24 pages)apply — Configure an Object
data class Notification(
var title: String = "",
var body: String = "",
var priority: Int = 1,
var soundEnabled: Boolean = true
)
val notification = Notification().apply {
title = "New message"
body = "You have 3 unread messages"
priority = 2
soundEnabled = false
}
println(notification)
// Notification(title=New message, body=You have 3 unread messages, priority=2, soundEnabled=false)also — Side Effect Without Changing Object
val numbers = mutableListOf(1, 2, 3)
.also { println("Before: $it") }
numbers.add(4)
numbers
.also { println("After: $it") }
.removeIf { it % 2 == 0 }
.also { println("Odd only: $numbers") }Chaining Scope Functions
data class User(var name: String, var email: String, var verified: Boolean = false)
fun createUser(name: String, email: String): User =
User(name, email)
.apply { verified = name.isNotBlank() && email.contains("@") }
.also { println("Created user: $it") }
fun main() {
val user = createUser("Bob", "bob@mail.com")
println("Verified: ${user.verified}")
}Choosing the Right Function
You want to… Use
──────────────────────────────────────────────────────────
Run code only when not null ?.let { }
Compute a value using multiple steps run { }
Call many methods on an existing object with(obj) { }
Set up properties on a new object obj.apply { }
Log or inspect without changing return obj.also { }
Practical Example: HTTP Request Builder
data class HttpRequest(
var url: String = "",
var method: String = "GET",
var headers: MutableMap = mutableMapOf(),
var body: String? = null
)
fun buildRequest(configure: HttpRequest.() -> Unit): HttpRequest =
HttpRequest().apply(configure)
fun main() {
val request = buildRequest {
url = "https://api.example.com/users"
method = "POST"
headers["Authorization"] = "Bearer token123"
headers["Content-Type"] = "application/json"
body = """{"name":"Alice","role":"admin"}"""
}
with(request) {
println("$method $url")
headers.forEach { (k, v) -> println(" $k: $v") }
println(" Body: $body")
}
} 