Kotlin — Scope Functions

Destan Erik
Ekmob Developer Studio
4 min readJan 19, 2020
A modern building in Palma de Mallorca, Spain. Taken by me :)

In this article, I’m going to examine the scope functions that make developers’ life a little easier. First of all, I would like to make a simple description of scope functions.

What are the Scope Functions?

These are five of them: let, run, with, applyand also. The main purpose of behind the scope functions is to execute a block of code within the context of an object. They all take almost the same task, but with minor nuances. First I’m going to try to define the distinctions between each of them below.

What is the main distinctions between each of them?

There are two main differences between each scope function:

  1. The first one is the way to refer to the context object

— There are two ways to access the context object. One of them is a lambda receiver (this) and the another one is a lambda argument(it).When we call the such functions, we can access the object without its name by using one of them. In short, if we want to group these scope functions according to ways of access the context object, we can group them as follows.

  • run , with and apply can access inside of an object’s scope by using this.
  • let and also can access inside of an object’s scope by using it.

2. The second one is return value of scope function

— Each scope function has a return value.

  • apply and also return the context object.
  • let , run and with return the lambda result.

Let’s examine each function below.

let

When we are using letin code, we can access inside of an object’s scope by using it and the return value is the lambda result . Here is some code:

val person = Person("Zed", 50)
val result = person.let { it.age * 5 }
println(person)
println(result)

Output is:

Person(name=Zed, age=50)
250

As you can see the result is the lambda result. We can also use let function one after another like chain.Here is a sample of that usage:

val person = Person("Zed", 50)
val result = person.let {it.name.length}
.let { it * 2 }
.let{it - 1}
println(result)

Output is:

5

Don’t forget each let function return lambda result.We can write the same code using a single letfunction.

val person = Person("Zed", 50)
val result = person.let {
val length = it.name.length
val multipliedByTwo = length * 2
multipliedByTwo - 1}
println(result)

Output is:

5

We can also use letto do a null check most of time.

var str: String? = null
var result = str?.let { it.length } ?: -1
println(result)

Output is:

-1

By the way, we can use the method reference (::) instead of lambda like that:

val person = Person("Zed", 50)
person.let {it.name.length}
.let(::println)

Output is:

3

also

When we are using alsoin code, we can access inside of an object’s scope by using it and the return value is the object itself unlike the let . We can use also for additional actions that don’t modify the object, such as logging. Here is a simple sample:

var list = mutableListOf("one", "two", "three")list.also{
println("The list size before adding new one: ${it.size}")
}.add("four")
println("The list size after adding new one: ${list.size}")

Output is:

The list size before adding new one: 3
The list size before adding new one: 4

We couldn’t write the same code with let. Because letreturn the lambda result, not the list object. So we couldn’t add a new item into the list after using let.

with

When we are using within code, we can access inside of an object’s scope by using this and the return value is the lambda result . This function can take one argument unlike others because it’s a non-extension function. Here is a simple sample that shows the usage:

var person = Person("Zed", 50)
println(person)
with(person){
name = "No Name"
this.age= 26 //'this' is optional
}
println(person)

Output is:

Person(name=Zed, age=50)
Person(name=No Name, age=26)

This function can be useful when you want to modify object or call object methods.

run

When we are using runin code, we can access inside of an object’s scope by using this and the return value is the lambda result . run does the same as with but invokes as let. Here is a simple sample:

val person = Person("Zed", 50)
val result = person.run {
val length = name.length
val multipliedByTwo = length * 2
multipliedByTwo - 1}
println(result)

The difference of the run function from the let function is that it doesn’t need the keyword it when accessing the object.

apply

When we are using applyin code, we can access inside of an object’s scope by using this and the return value is the object itself . apply does the same as also but the difference of the apply function from the also function is that it doesn’t need the keyword it when accessing the object. Here is a simple sample:

var list = mutableListOf("one", "two", "three")list.apply{
println("The list size before adding new one: ${size}")}.add("four")
}.add("four")
println("The list size after adding new one: ${list.size}")

Output is:

The list size before adding new one: 3
The list size after adding new one: 4

Conclusion

To sum up, in this article I tried to simplify the outline of the scope functions which are complicated at the first sight in the official document. I hope it was useful for you :)

--

--