Kotlin Scope Functions
Tags: kotlin, tutorial, programming, jvm
Published: 2020-04-26 09:23:13

# Scope Functions
## run, with, let, apply, also
---
### First things first, They are **_higher order functions_**

#### But... What are higher order functions anyways
On a very basic level they execute a function which is supplied to it. The Kotlin standard library is full of higher order functions. One of them being [repeat](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/repeat.html).

```
    public inline fun repeat(times: Int, action: (Int) -> Unit)
```
The repeat function takes an action as an argument and returns Unit, but a higher order function can return any object. If you would want to invoke repeat, you would have to pass a function to this which will be invoked by the _repeat_ function.
e.g.
```
    repeat(3) {
        println("current value $it")
    }
```

If you would want to write my very own higher order repeat function you can follow the same syntax.

```
    fun <T> T.myFunction(action: (T) -> String) {
        ...
    }
```

So, with that knowledge we can approach scope functions knowing they are higher order functions provided by the Kotlin standard library just like [repeat](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/repeat.html).

- **run**
Takes an expression which is a piece of code, and executes it. This may or may not return a value. The whole idea of the run scope function is to _run_ a piece of code.

```
    data class Person(
        var name: String,
        var age: Int,
        var job: String
    ) {
        fun printPerson() = println(this)
    }
    
    val john = Person("John Doe", 20, "Plumber")
    val jane = Person("Jane Doe", 20, "Waitress")

    john.run {
        if (age > jane.age) println("John")
    }

    john.run {
        if (age > jane.age) this else jane
    }.printPerson()

    run {
        if (john.age > jane.age) john else jane
    }.printPerson()

```

- **with**
Exactly the same as _run_, the only difference being it takes a receiver as an input. It is the same as calling run with an object instead of an argument.

```
    with(john) { // similar to the run function being called with the object
            age += 1
            "Age is $age"
        }.printThis()
```

- **let**
_let_ takes a 'this' value and performs operations on _it_ (see what I did there..) and may or may not return a value.

```
    john?.let {
            it.age += 1
            "Age is ${it.age}"
        }.printThis() ?: defaultValue()

```
_let_ is most useful when used with Kotlin's null checks. This allows you to execute a piece of code only when an object exists.

- **apply**
It takes an object argument and returns the same object. It is very useful when we are transforming the object.

```
    jane.apply {
        jane.age = 22
        jane.job = "Restaurant Owner"
    }.printPerson()
```

Another use case of _apply_ is to initialize objects. In the below case the property name can be [lazily initialized](https://kotlinlang.org/docs/reference/properties.html) or transformed using apply.

```
    val apply = Planet().apply {
        name = "Earth"
    }
```

- **also**
Is similar to _apply_, the only difference being _it_ receives a lambda which is accessed within the lambda with a name.

```
   val apply = Planet().also {
        it.name = "Earth"
    } 
```