Kotlin Scope Functions

A diagram to help you to choose the right one!

Do we really need yet another blog post on Kotlin scope functions?

As a matter of fact I think we do. While there are many posts to describe them, I found none on how to pick up the correct function and when you should consider to refactor your code into using one of them.

I’ve tried to make it clear with a diagram:

Use this means we are going to (mostly) call methods on a single object, the reference to the object is this.

Pass it means we need to pass the object to other methods/functions, the reference to the object is it.

Result means we will have a result value from the block and we need to return it.

Side-effects means we don’t expect a result but we want to call for some methods that return Unit (a.k.a. void) and maybe combine them in Fluent Interface style.

So if you are wondering how to simplify some code where an object (the target object) is used multiple times, you should ask yourself two questions:

Do I need to call methods on this object or do I need to pass it as arguments to other methods/function?
Do I have a result at the end? Or am I only changing the state?

apply is useful if you need to call several methods on an object without caring for their return, like setters. Like the other 3 Extended Functions if the target object is null the block won’t be called and inside the block the object is always of a non nullable type. For example:

fun updateItem(itemId: String, newName: String, newPrice: Double) = itemsMap.get(itemId)?.apply { 
enabled = true
desc = newName
price = newPrice
}

with works almost exactly as apply, the only difference is that if the object of a nullable type, then it will call the block anyway passing the object as nullable. I use it only when there is a long block of code with some methods calling on a specific object, particularly if the var has a long name. For example:

// ... long method
val total = with(ridicurioslyLongNameOfFrameworkSingleton){
setSomething(a)
setSomethingElse(b)
// do other things...
getTotal()
}
// ...

let is probably the most used, at least by me. It is particularly useful together with the question mark, to obtain a result if a value is not null. You should consider to remove any code that resemble if (obj == null) {...} and use let or another scope function. For example:

fun fullName(firstName: String, middleName: String?, familyName: String) = middleName?.let{ 
"$firstName ${it.first()}. $familyName"
} ?: "$firstName $familyName"

run is useful if at the end calling a few methods on the object and then one that return something else. For example:

fun cartTotal(cart: Cart, items: List<CartItem>) = cart.run{ 
items.foreach{addItem(it)}
calcTotal()
}

also is useful if you have a value that you need to return, but before returning you also need to use it to do something else. For example:

fun createUser(userName: Stirng): Int =
myAtomicInt.getAndIncrement().also { users.put(it, userName) }

As reference these are the signatures:

public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
public inline fun <T, R> T.run(block: T.() -> R): R = block()
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }

“I don’t see any advantage in using them, it’s just messing things around” is a quite common objection that I heard when I show code using them to people not used to Kotlin.

The lesser advantage of using a scope function is save some lines of code. The greater advantage is to avoid temporary variables.

Temporary variables are usually a bad idea in Functional Programming style and they are a source of small bugs when you confuse them or you copy paste some code around (which of course none of us ever do!).

In Functional Programming style is also better (in the sense of transmitting a more clear intention) to use the single expression declaration form (that is fun f = …) for functions, and scope functions make it easier.

If you like this post please clap it and follow me on medium and twitter, thanks.

Like what you read? Give Uberto Barbini a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.