Kotlin Scope Functions: Expressing Intent with apply vs also
Stop guessing which scope function to use. Master the human intent, avoid common context traps, and write idiomatic Kotlin.
Kotlin Scope Functions: Expressing Intent with apply vs also

Kotlin Scope Functions: Expressing Intent with apply vs also
Not a Medium Member? “Read For Free”
If you are diving into Kotlin, you’ve likely run into its famous scope functions: let, run, with, apply, and also. They are fantastic for writing clean, idiomatic code. However, two of them constantly cause confusion: **apply and `also`**.
They look nearly identical on the surface. Both take an object, let you execute a block of code, and then return that exact same object. So, how do you choose between them?
🧠 The Golden Rule:
Scope functions are about expressing human intent, not enforcing compiler behavior. Use **apply when you are configuring an object, and `also`* when you are observing or reacting* to it.
🔄 The Before vs. After Refactor
To understand why these functions matter, look at how much boilerplate they eliminate while grouping related logic together.
// ❌ Without scope functions (Fragmented and verbose)
val setupUser = User()
setupUser.name = "Alex"
setupUser.role = "Admin"
println("Created user: ${setupUser.name}")
// ✅ With apply + also (Unified and intentional)
val user = User().apply {
name = "Alex"
role = "Admin"
}.also { logger.info("Created user: ${it.name}") }
1. apply — The Object Configurator
When you use apply, you are telling whoever reads your code: "Take this object, apply these settings to it, and then give it back to me."
Inside the apply lambda, the object becomes this (the implicit receiver). You can omit this entirely and access the object's properties and methods directly.
Kotlin Configuration Example
Imagine you are setting up a notification builder for a mobile app:
class Notification {
var title: String = ""
var message: String = ""
var importance: Int = 0
fun send() = println("Notification Sent: [$title] - $message")
}
fun main() {
// Expressing Intent: We are configuring this object immediately after creation
val urgentAlert = Notification().apply {
// 'this' is implicit here. We change properties directly.
title = "Security Alert"
message = "Unusual login attempt detected."
importance = 5
}
urgentAlert.send()
}
💡 Mental Shortcut: If your block reads like a constructor, you’re using apply correctly.
2. also — The Side-Effect Companion
When you use also, you are telling the reader: "Take this object, perform a side-effect without altering the main flow, and then hand it back."
Inside the also lambda, the object is referred to as it (the argument).
⚠️ Can also mutate an object?
Technically, yes. The compiler will absolutely allow you to modify properties using it.property = value. However, doing so is highly discouraged because it makes intent unclear and hurts readability. Developers reading your code expect also to indicate an independent side-effect, not a primary mutation.
// ✅ Explicit and clean intent:
val user = User("tech_guru", "guru@example.com").also { user ->
// Explicitly naming 'it' to 'user' reinforces clarity
println("Database Audit Log: New user created for ${user.username}")
}
💡 Mental Shortcut: If removing the block completely doesn’t change the underlying state of the object, you’re using also correctly.
🛠️ Staff Insight: Tracing Data Flow in Functional Pipelines
Because also takes it as an argument and returns the exact same object without disrupting the return type, it is a powerful tool for debugging and tracing data flow. You can tap into a pipeline mid-stream, view the data, and let it pass through completely unhindered:
val result = fetchData()
.also { println("Raw Data Received: $it") } // Tapped into pipeline
.map { transform(it) }
.also { println("Transformed Data: $it") } // Verified the output
⚠️ Common Mistakes & Intent Confusion
When you use the wrong scope function, your code still compiles, but it sends conflicting signals to your team.
// ❌ Misusing apply for side-effects
user.apply {
println("User created: $username") // Feels wrong: 'apply' implies configuration
}
// ❌ Overusing also for mutation
user.also {
it.name = "Updated" // Intent confusion: 'also' implies an observation or log
}
The Fix: Swap them. Use apply to change the name, and use also to print the log statement.
☠️ Trap: Nested Scope Functions
Nesting scope functions that share the same context variable name is a recipe for catastrophic bugs.
// ❌ The Hidden Context Trap
user.apply {
profile.apply {
// Quick quiz: Which 'name' are we changing? User or Profile?
name = "John"
}
}
Because both blocks use an implicit this, tracking which object is being mutated becomes incredibly confusing.
The Fix: Break the chain using also with an explicit, named parameter.
// ✅ Context is crystal clear
user.apply {
profile.also { profile ->
profile.name = "John" // No ambiguity
}
}
🧠 Mental Model Shortcuts
Bookmark these four quick check-rules to guarantee you always select the right function:
- ✔ Use
applyif you are building or configuring the object. - ✔ Use
alsoif you are touching the object without changing its core purpose. - ✔ If removing the block breaks the object initialization → you used
applycorrectly. - ✔ If removing the block leaves the object perfectly usable → you used
alsocorrectly.
The Ultimate Scope Function Cheat Sheet

The Ultimate Scope Function Cheat Sheet
🙋 Frequently Asked Questions (FAQs)
Is there a performance difference between apply and also?
No. Both are implemented as inline functions in Kotlin. The compiler copies the bytecode directly into the call site at compile time, meaning there is zero runtime overhead or extra object allocation.
How do I remember this vs it?
Think of **apply as working inside the object's skin (this). Think of `also** as standing next to the object, pointing atit` from the outside.
💬 What Do You Think?
- Have you ever inherited a codebase where
alsowas heavily abused to mutate objects? How did it affect your readability? - Do you use the pipeline-tapping
.also { println(it) }trick when debugging, or do you stick entirely to traditional IDE breakpoints?
Let’s discuss in the comments below!
🔚 Conclusion
Kotlin doesn’t force you to choose the right scope function — but your teammates will feel the difference. Writing idiomatic Kotlin isn’t just about what works, but about what communicates your intent clearly.
📘 Master Your Next Technical Interview
Since Java is the foundation of Android development, mastering DSA is essential. I highly recommend “Mastering Data Structures & Algorithms in Java”. It’s a focused roadmap covering 100+ coding challenges to help you ace your technical rounds.
- E-book (Best Value! 🚀): **$1.99 on Google Play**
- Kindle Edition: **$3.49 on Amazon**
- Also available in Paperback & Hardcover.
메타데이터
- post_id
- 84df8991808c
- slug
- kotlin-scope-functions-expressing-intent-with-apply-vs-also-84df8991808c
- url
- https://medium.com/@sivavishnu0705/kotlin-scope-functions-expressing-intent-with-apply-vs-also-84df8991808c
- canonical_url
- https://medium.com/@sivavishnu0705/kotlin-scope-functions-expressing-intent-with-apply-vs-also-84df8991808c
- author_url
- https://medium.com/@sivavishnu0705
- status
- ok
- fetched_at
- 2026-06-24 11:06:28