Kotlin Scope Functions: When to use them
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.
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, wherelet
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.
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.
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.
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.
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:
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:
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:
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.