# Extension Functions and Scope Functions — Kotlin

Source: https://www.skillbyai.com/en/kotlin/fn-extensions

> Add functions to types; let, apply, run, also, with.

## Extending without inheritance

An **extension function** `fun String.isEmail(): Boolean` adds a function callable as `"a@b.c".isEmail()` without modifying or subclassing `String`; it is resolved **statically** and cannot access private members. Extension properties work similarly. The **scope functions** run a block with an object in context: `let` (object as `it`, returns the lambda result; common with `?.let` for nullable values), `also` (as `it`, returns the object; side effects such as logging), `apply` (as `this`, returns the object; configuration), `run` (as `this`, returns the lambda result) and `with(obj) { }` (non-extension form of `run`).

## Extensions and scope functions together

Pick the function by what it returns.

```kotlin
fun String.isEmail(): Boolean = contains("@") && substringAfter("@").contains(".")

val List<Int>.median: Double?
    get() = if (isEmpty()) null else sorted().let { s ->
        if (s.size % 2 == 1) s[s.size / 2].toDouble()
        else (s[s.size / 2 - 1] + s[s.size / 2]) / 2.0
    }

class Request { var url = ""; var timeoutMs = 0; val headers = mutableMapOf<String, String>() }

fun build(token: String?): Request = Request().apply {   // configure, returns Request
    url = "https://api.example.com"
    timeoutMs = 5_000
    token?.let { headers["Authorization"] = "Bearer $it" }  // only if not null
}.also { println("built request for ${it.url}") }           // side effect, returns Request
```

## Do not nest scope functions deeply

Chains of `let` inside `apply` inside `run` quickly become unreadable because `it` and `this` change meaning. If a block grows, extract a named function.

**Quiz:** Which scope function returns the object itself and exposes it as `this`?

- [ ] `let`
- [x] `apply`
- [ ] `run`
- [ ] `with`

*Answer:* `apply`. apply is for configuring an object; also returns it too but uses it.
