Kotlin’s scope functions (let, run, with, apply, and also) are powerful tools that help you write more concise and expressive code. They allow you to perform operations on objects within a temporary scope, making your code cleaner and more readable.


What Are Scope Functions?

Scope functions are extension functions that execute a block of code on an object and return a result (either the object itself or a computed value). They differ in:

  • Receiver (this vs. it)
  • Return value (object vs. result of lambda)

The 5 Scope Functions

Here’s a quick comparison:

FunctionReceiver (this/it)Return ValueCommon Use Case
letit (explicit)Lambda resultNull checks, transformations
runthis (implicit)Lambda resultObject initialization + computation
withthis (implicit)Lambda resultGrouping operations on an object
applythis (implicit)Object itselfConfiguring objects
alsoit (explicit)Object itselfSide effects, logging

1. let – Safe Calls & Transformations

Use Case: Null checks and transformations.

Example: Safe Call on Nullable Object

val name: String? = "Kotlin"
 
name?.let { 
    println(it.uppercase())  // "KOTLIN"
}
  • it refers to the object (name).
  • Returns the result of the lambda.

Example: Chaining Operations

val numbers = listOf(1, 2, 3)
numbers
    .filter { it > 1 }
    .let { filtered -> println(filtered) }  // [2, 3]

2. run – Object Initialization & Computation

Use Case: Running operations on an object and returning a result.

Example: Compute a Value

val result = "Kotlin".run {
    println(this.length)  // 6
    this.uppercase()      // Returns "KOTLIN"
}
  • this refers to the receiver ("Kotlin").
  • Returns the lambda result.

Example: Initialize & Compute

val user = User().run {
    name = "Alice"
    age = 25
    this  // Returns the configured User object
}

3. with – Grouping Operations

Use Case: Performing multiple operations on an object without chaining.

Example: Modifying a List

val numbers = mutableListOf(1, 2, 3)
 
with(numbers) {
    add(4)
    remove(2)
    println(this)  // [1, 3, 4]
}
  • this refers to numbers.
  • Returns the lambda result.

4. apply – Object Configuration

Use Case: Initializing or configuring objects (builder pattern).

Example: Configuring a View

val button = Button(context).apply {
    text = "Click Me"
    textSize = 16f
    setOnClickListener { /* ... */ }
}
  • this refers to the Button.
  • Returns the object itself (button).

5. also – Side Effects & Logging

Use Case: Performing additional actions without altering the object.

Example: Logging

val list = listOf(1, 2, 3)
    .also { println("Original list: $it") }  // Logs before mapping
    .map { it * 2 }
  • it refers to the list.
  • Returns the original object.

When to Use Which?

ScenarioRecommended Function
Null checkslet
Transforming datalet, run
Object configurationapply
Side effects (logging)also
Grouping operationswith

Key Takeaways

  1. let → Safe calls, transformations.
  2. run → Compute a value from an object.
  3. with → Group operations on an object.
  4. apply → Configure objects (builder pattern).
  5. also → Side effects (logging, debugging).

Scope functions make Kotlin code more concise and expressive. Use them wisely to improve readability! 🚀