Kotlin Scope Functions: When to use them

Raphael De Lio
Kotlin with Raphael De Lio
6 min readJul 24, 2023

Twitter | LinkedIn | YouTube | Instagram
This video is also available as a YouTube video. Watch it here.

Today, we’re diving into one of Kotlin’s most powerful features: Scope Functions. These functions are a unique aspect of Kotlin, and they can make your code more concise and readable. So, let’s get started!

What are scope functions?

First off, what exactly are scope functions? Well, in Kotlin, there are five scope functions: let, run, with, apply, and also. These functions are called on an object, and then within the scope of that function, you can access the object without its name.

This might sound a bit abstract now, but don't worry, we'll go through each one in detail.

The LET function

Let’s start with the let function. The let function is used when you want to perform operations on an object and then return a result. The object is accessible within the let block as it (or another name if you choose to define one).

This can be particularly useful when you want to perform transformations on an object and use the result. The operations within the let block are localized to that block and do not affect the object outside of it, which can make your code safer and more predictable.

Also, it allows you to define a variable for a certain scope without polluting the outer scope.

Try it out! Press the play button!

In this example, we called let on the name variable. Inside the let block, we reversed the string.

However, this change doesn't affect the original name variable. The it keyword refers to the object the let function was called on.

Fun fact:

Many people believe that the purpose of let is handling nullable values. Even though that's a common use case, it wasn't specifically designed to handle nullable values. It's a scope function that allows you to create a temporary scope where you can work with an object, perform operations on it, and return a result.

In many programming languages, the keyword let is used to introduce a new binding or assignment, essentially saying "let this variable be equal to this value." This is particularly common in functional programming languages, where let is used to create a new scope and bind values to variables within that scope.

The term let is derived from mathematical logic and set theory, where it's used to introduce a new variable or constant. For example, in mathematics, you might say "let x be a number such that x > 0," which introduces x as a new variable and defines a condition for it.

In programming languages, let serves a similar purpose. It introduces a new variable and assigns a value to it. The scope of this variable is usually limited to the block of code where it's defined, which helps prevent variable naming conflicts and makes the code easier to understand.

So, the reason let is used in programming languages is largely historical and based on its usage in mathematics. It's a convention that has been adopted and carried forward in many different languages.

The RUN function

Next up is the run function. run is similar to let, but there's a key difference: the object is then accessible within that block without its name or reference. And it is also used when you need to execute a block of code on an object and then return a result.

This can be particularly useful when you're working with an object with many properties or functions, as it can make your code more concise and readable.

Try it out! Press the play button!

In this example, run is called on the user object. Inside the run block, we're able to call methods on user directly, without using its name. The last expression within the run block is the result of the run function, which in this case is a string indicating the result of the operations.

The WITH function

The with function is a bit different from let and run. Instead of being called on an object, with takes an object as a parameter. Inside a with block, you can access the object directly, just like in run. Here's an example:

It is used when you want to perform multiple operations on a non-null object without having to repeatedly refer to the object. It's similar to run, but instead of being called on an object, it takes the object as a parameter.

And it is particularly useful when you're working with an object that has several properties or methods that you need to access in a sequence. It allows you to access the object's members directly, without needing to refer to the object each time.

Try it out! Press the play button!

In this example, with is used to perform multiple operations on the stringBuilder object. Inside the with block, we can call append directly, without needing to refer to stringBuilder each time. This can make your code more concise and readable, especially when you're working with objects that have many properties or methods.

The APPLY function

Moving on, let’s discuss the apply function. apply is a bit special because it allows you to initialize or configure an object and then return it. It's particularly useful when you're setting up an object with multiple properties or configurations.

The apply function is called on an object, and inside the apply block, you can access the object's properties and methods directly using this. The object itself is then returned from the apply block.

Try it out! Press the play button!

In this example, apply is used to configure the dbConnection object. Inside the apply block, we can set the properties directly, without needing to refer to dbConnection each time.

This makes the code more concise and readable, especially when you're setting up complex objects with many properties or configurations.

The dbConnection object, with all the applied changes, is then returned from the apply block.

After the apply block, we call the connect method to establish the database connection.

The ALSO function

Last but not least, we have the also function. also is similar to let, but it returns the original object instead of the result of the lambda.It’s particularly useful when you want to do something with the object right after initializing or transforming it.

The also function is called on an object, and inside the also block, you can access the object using it. The object itself is then returned from the also block.

Try it out! Press the play button!

In this example, apply is first used to set up the report object. Then, also is used to perform an additional operation: printing a message and saving the file. Inside the also block, we refer to the report object using it. The report object, with all the applied changes, is then returned from the also block.

This is a common use case for also: performing a side-effect (like logging or saving to a database) that doesn't affect the main operation.

Chaining Scope Functions

One of the powerful features of Kotlin’s scope functions is that they can be chained together. This allows you to perform complex operations in a readable and concise way. Let’s look at some examples of how this can be done.

Chaning let and also

Suppose you have a nullable string that you want to manipulate and then log the result:

Try it out! Press the play button!

In this example, let is used to perform transformations on the string, and then also is used to log the result.

Chaining apply and run

Consider a scenario where you’re setting up a DatabaseConnection object and then want to execute a block of code:

Try it out! Press the play button!

Chaining with and also

Suppose you’re working with a Calendar instance and you want to print today's date and also return the day of the week:

Try it out! Press the play button!

In this example, with is used to perform operations on the Calendar instance and return the day of the week, and then also is used to log the day of the week.

As you can see, chaining scope functions can make your code more readable and concise, especially when you’re performing a series of related operations. However, keep in mind that overusing this feature can make your code harder to understand, so use it wisely.

Conclusion

And that’s a wrap on Kotlin’s scope functions!

As we’ve seen, these functions are a powerful tool that can make your code more concise and readable. Whether you’re initializing complex objects, performing additional operations, or transforming objects, scope functions have you covered.

So next time you’re writing Kotlin code, give scope functions a try — you might be surprised at how much they can improve your code.

Contribute

Writing takes time and effort. I love writing and sharing knowledge, but I also have bills to pay. If you like my work, please, consider donating through Buy Me a Coffee: https://www.buymeacoffee.com/RaphaelDeLio

Or by sending me BitCoin: 1HjG7pmghg3Z8RATH4aiUWr156BGafJ6Zw

Follow Me on Social Media

Stay connected and dive deeper into the world of Kotlin with me! Follow my journey across all major social platforms for exclusive content, tips, and discussions.

Twitter | LinkedIn | YouTube | Instagram

--

--