Scope functions in Kotlin are standard functions (let, also, run, apply, with) that allow managing the execution context of a block of code for an object. They differ in:
it or this.| Function | this/it | Returns | For what? |
|---|---|---|---|
| let | it | result | chain of operations, working with nullable, map |
| also | it | object | side effects, logging, debug |
| run | this | result | calculations, initialization with return |
| apply | this | object | object configuration, builders |
| with | this | result | working with external APIs, object "outside" |
Examples:
let: convenient if the object is nullable:val str: String? = "Text" str?.let { println(it.length) }
apply: configuring an object:val paint = Paint().apply { color = Color.RED strokeWidth = 2f }
run: executing on an object, returning the result:val length = "abcde".run { length }
with: for working with an external object:val sb = StringBuilder() with(sb) { append("Hello, ") append("world!") toString() }
also: for side effects (e.g., logs):val list = mutableListOf(1, 2, 3) list.also { println("Before: $it") }.add(4)
it, changing object properties is not very convenient.this / it), useful for builders.with is a regular function, not an extension.What is the difference between let and also?
Answer:
it inside the block,let returns the result of the lambda, often used for transformation chains,also returns the original object, used for side effects (log, debug) to avoid interfering with the transformation chain.Example:
val result = listOf(1).also { println(it) }.map { it * 2 } // result — List<Int>
Story
A beginner used let to configure an object, thinking that this way it would change its state in a "chain." As a result, upon completion of the configuration block, they obtained not an object but the result of the lambda (e.g., nothing), violating the DSL building chain.
Story
When writing code to work with nullable objects, run was used instead of let, not noticing the difference in return value. As a result, the expression's value differed from expected, resulting in null where it shouldn't be — breaking the application's logic.
Story
In a large builder, with was accidentally used for internal objects, expecting the extension pattern. Since with is not an extension function, the chain of several with blocks did not work correctly, internal calls got confused and went beyond the actual object. The hierarchy of object creation had to be completely rewritten.