Kotlin Scope Functions - let, also, run, apply, with

Kotlin Scope Functions - let, also, run, apply, with

Scoped Functions

The Kotlin standard library is full of scope functions that allow you to perform any given logic in the scope of an object, without referencing the object by its name. These functions allow for concise code, because it reduces a lot of the verbosity you'd have if you attempted to do similar operations in a language like Java. Whether you're starting out with Kotlin, or have been using it for awhile, it can be hard to remember all the scope functions.

this or it?

In most object oriented programming languages, Kotlin included, there's a concept of this in regards to an object's self. Kotlin's technical name in scope functions you access the object as a lambda receiver (this), or as a lambda argument (it). The functions apply, run and with all perform actions on a object as a lambda receiver (this), with let and also use a lambda argument (it).

We'll show exactly how this works with an example about pets. Here's a data class of a pet and our starting pet named Ajax is 4 years old. All these examples show how to update Ajax's age using the different scope functions. For these examples we'll explicitly specify the return type to highlight the difference between each function.

image.png

Returning the Scoped Object

We'll look at apply first. Notice that the age field is being access directly. That's because no matter what happens inside the lambda, the result will always be the pet object. This happens because apply references the pet object as a lambda receiver (this).

image.png also behaves similarly. Except we reference pet as a lambda argument (it).

image.png Those are the only two that always return pet object we initially referenced.

Returning the Lambda Result

The scope functions run, with, and let all return the result of the lambda.

With let we reference a lambda argument (it) as well, but you can see the return type is different. That's because the return type is now the lambda result. As you can see the assignment of age to 5 doesn't return anything so pet will be of type Unit. You can see in the second example, the return type changes base on the return type of the final line of the lambda.

image.png

That behavior is like run except it uses a lambda receiver (this).

image.png

run also has a second functionality, where you can use it stand alone. image.png

Then there is with. It behaves like the first run example. It references this and returns the lambda result. It's different because it takes the pet object as an argument for the function. image.png

As you can see they all operate differently, but you can use a few interchangeably. Here's how you could rewrite the first run example using let. image.png

There aren't hard and fast rules on when you should one or another. In the end it can come down to personal preference. As someone who codes Kotlin everyday, of these scope functions, I use let, apply, and also the most.

Here's a diagram to save and reference.

07B22915-410C-4410-BD6D-94A2DB004077.PNG

If you like what you've read, want to continue the discussion, or have anything else on your mind, reach out to me on Twitter with your favorite scope function.