0Pricing
Kotlin Academy · Lesson

also: Side Effects Without Changing the Receiver

Use also for logging, debugging, and side-effect-only operations.

also: Side Effects Without Changing the Receiver is a free Kotlin Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Kotlin Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

also at a Glance

also runs a block with the receiver as it and returns the receiver unchanged. Perfect for side effects — logging, debugging, validation — without disrupting a chain.

Basic also

also { ... } performs a side effect and yields the same value back, so calls flow naturally.

fun main() {
    val nums = listOf(1, 2, 3, 4)
        .also { println("input: $it") }
        .filter { it % 2 == 0 }
        .also { println("filtered: $it") }
    println(nums)
}

also for Logging

Add log statements mid-pipeline without breaking the flow.

fun main() {
    val processed = "  Hello, Kotlin!  "
        .also { println("raw: '$it'") }
        .trim()
        .also { println("trimmed: '$it'") }
        .uppercase()
    println(processed)
}

also for Debugging

Use also to inspect intermediate state during a refactor — drop it later without changing the chain.

fun main() {
    val sum = (1..5).toList()
        .also { println("list: $it") }
        .sum()
        .also { println("sum: $it") }
    println("final: $sum")
}

also vs apply

apply uses this (better for assignments). also uses it (better for explicit references and side effects).

class Counter {
    var value: Int = 0
}
fun main() {
    val c = Counter().apply { value = 10 } // uses this implicitly
    val d = Counter().also { it.value = 10 } // uses it explicitly
    println("${c.value} / ${d.value}")
}

also for Initialization Side Effects

Useful when you need to do something with an object during creation that isn't setting a property.

class Repository {
    val cache = mutableMapOf<String, String>()
    fun init() = println("Repository initialized")
}
fun main() {
    val r = Repository().also { it.init() }
    r.cache["key"] = "value"
    println(r.cache)
}

also Returns Original

The receiver passes through unchanged — easy to chain.

fun main() {
    val len = "Hello".also { println("got: $it") }.length
    println(len) // 5
}

also for Validation

Run assertions mid-chain.

fun main() {
    val n = listOf(1, 2, 3, 4)
        .filter { it > 0 }
        .also { require(it.isNotEmpty()) { "must have positives" } }
        .sum()
    println(n)
}

Combining also and let

Use also for side effects and let for transformation in the same pipeline.

fun main() {
    val raw: String? = "  42  "
    val n = raw
        ?.also { println("raw: '$it'") }
        ?.let { it.trim().toIntOrNull() }
        ?.also { println("parsed: $it") }
    println(n)
}

also for Adding to a Collection

Add a constructed object to a list inline.

class User(val name: String)
fun main() {
    val users = mutableListOf<User>()
    val ada = User("Ada").also { users.add(it) }
    val ben = User("Ben").also { users.add(it) }
    println(users.map { it.name })
}

When to Use also

Reach for also when you need a side effect (log, validate, register) but you want the original value to flow through.

fun main() {
    val total = listOf(10, 20, 30, 40, 50)
        .also { println("count: ${it.size}") }
        .filter { it >= 30 }
        .also { println("kept: $it") }
        .sum()
    println("total: $total")
}

Anti-Pattern: also for Reassignment

Don't use also when you actually need to transform — that's what let/run are for.

fun main() {
    val name = "ada"
    // BAD: also returns receiver, so upper is wrong
    // val upper = name.also { it.uppercase() } // upper == "ada"
    val upper = name.let { it.uppercase() }
    println(upper) // ADA
}

Quick Check

What value does also return after executing its block?

Recap

also runs a side-effect block with it and returns the receiver. Use it for logging, debugging, validation, registration — anywhere you need a peek without altering the value. For transformations, use let or run instead.

Frequently asked questions

Is the “also: Side Effects Without Changing the Receiver” lesson free?

Yes — the full text of “also: Side Effects Without Changing the Receiver” is free to read here on the web, and the Kotlin Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Kotlin Academy course, upgrade to CoddyKit PRO.

What will I learn in “also: Side Effects Without Changing the Receiver”?

Use also for logging, debugging, and side-effect-only operations. You practise Kotlin Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Kotlin Academy?

No prior experience is required. Kotlin Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “also: Side Effects Without Changing the Receiver” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Kotlin Academy lesson?

Yes. Every Kotlin Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. apply and with: Configure and Return
  2. let and run: Transform and Scope
  3. also: Side Effects Without Changing the Receiver
  4. Choosing the Right Scope Function: Decision Guide
← Back to Kotlin Academy