Diving into Kotlin .apply{} function
Tags: kotin, android, learning
Published: 2023-12-08 06:27:22

## Background
Since I started using Kotlin for Android development in 2017, I've been using functions like [apply](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/apply.html), [let](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/let.html), [also](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/also.html), and [run](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/run.html). These functions are super handy and make my code much easier to read. But here's the thing: How exactly do they work? What makes them so special?

## High-order functions
According to the [Kotlin doc](https://kotlinlang.org/docs/lambdas.html#higher-order-functions):

> A higher-order function is a function that takes functions as parameters, or returns a function.

Higher-order functions aren't something new in the programming world; yes, even in Java, they've been around since version 8, iirc. They promote reusability and conciseness. They've also unlocked easier implementations like `.apply{}` (the function we're delving into today) as well as famous asynchronous libraries such as [Coroutines](https://kotlinlang.org/docs/coroutines-overview.html).

So enough about the background, let's look at some code.

## Inside apply.{} function
_As I'm writing this, I was using `kotlin-stdlib-common-1.9.0`_
```kotlin
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}
```
There are three components I want to talk about which are:
- inline keyword
- contract
- Function literals with receiver

### inline keyword
So, when we use the inline keyword, we are telling the compiler to swap out the call-site with the actual code of the inline function. This little trick is to cut down on the extra work that high-order functions bring during the runtime phase. High-order functions aren't as lightweight as regular functions.

To illustrate how inline keyword work, let's take a look at javabyte code when the keyword is used:
```kotlin
inline fun foo1(block:() -> Unit) {
    print("Starting foo1 now!")
    block()
    print("Finished foo1!")
}

fun foo2(block:() -> Unit) {
    print("Starting foo2 now!")
    block()
    print("Finished foo2!")
}
```

And the main function that calls this method:
```kotlin
fun main(args: Array<String>) {
    print("Cheesy Coder")
    foo1 {
        print("I am running foo1 now!")
    }
    foo2 {
        print("I am running foo2 now!")
    }
}
```
Now, let's look at what the Java bytecode says (I will only display what matters the most since I want to make comparison between these two methods)
```
public final static main([Ljava/lang/String;)V
// Skipping until interesting part
   L1
    LINENUMBER 2 L1
    LDC "Cheesy Coder"
    ASTORE 1
   L2
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 1
    INVOKEVIRTUAL java/io/PrintStream.print (Ljava/lang/Object;)V
   L3
   L4
    LINENUMBER 3 L4
   L5
    ICONST_0
    ISTORE 1
   L6
    LINENUMBER 22 L6
    LDC "Starting foo1 now!"
    ASTORE 2
   L7
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 2
    INVOKEVIRTUAL java/io/PrintStream.print (Ljava/lang/Object;)V
// Skipping until interesting part

   L17
    LINENUMBER 24 L17
    LDC "Finished foo1!"
    ASTORE 2
   L18
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 2
    INVOKEVIRTUAL java/io/PrintStream.print (Ljava/lang/Object;)V
   L19
   L20
    LINENUMBER 25 L20
    NOP
   L21
    LINENUMBER 6 L21
    GETSTATIC MainKt$main$2.INSTANCE : LMainKt$main$2;
    CHECKCAST kotlin/jvm/functions/Function0
    INVOKESTATIC MainKt.foo2 (Lkotlin/jvm/functions/Function0;)V
   L22
    LINENUMBER 9 L22
    RETURN
```
The Java bytecode appears distinct when the inline keyword is used. Instead of invoking `foo1` within the main function, the implementation contents of `foo1` have been copied into the main function. Labels `L1` and `L2` illustrate the loading of the 'Cheesy Coder' string and the use of `java/io/PrintStream` to print the loaded string. Labels `L6` and `L7` represent the loading of the 'Starting foo1 now!' string and its subsequent printing. There is no mention of the reference to `foo1` method anywhere in this code. However, upon encountering `L21`, the instruction is to invoke `foo2` method.

Not only that but there are more Java bytecode created around `foo2`:
```
public final static foo2(Lkotlin/jvm/functions/Function0;)V
// Skipping implementation

final class MainKt$main$2 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function0 {


  // access flags 0x1041
  public synthetic bridge invoke()Ljava/lang/Object;
    ALOAD 0
    INVOKEVIRTUAL MainKt$main$2.invoke ()V
    GETSTATIC kotlin/Unit.INSTANCE : Lkotlin/Unit;
    ARETURN
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x11
  public final invoke()V
   L0
    LINENUMBER 7 L0
    LDC "I am running foo2 now!"
    ASTORE 1

// Skipping implementation
```
This represents the overhead cost. Not only `foo2` method was created but an additional class is also generated for its parameters. It's very interesting to see how the Java bytecode demonstrates the impact of the inline keyword!

### Contract
This API reminds me of [require](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/require.html) or [requireNonNull](https://docs.oracle.com/javase/8/docs/api/java/util/Objects.html#requireNonNull-T-). I have seen these kind of checks in many different codebase and it can _sometimes_ assure developers certain conditions are met.

Unlike above, `contract` tells information or hints to the compiler so that it can make informed decision how smartcast works or give warning to developers. In this function `.apply{}`, the contract is called `callsInPlace`. 

According to Kotlin docs:
> Specifies that the function parameter lambda is invoked in place.
This contract specifies that:
- the function lambda can only be invoked during the call of the owner function, and it won't be invoked after that owner function call is completed;
- (optionally) the function lambda is invoked the amount of times specified by the kind parameter, see the InvocationKind enum for possible values.
A function declaring the callsInPlace effect must be inline.

It's a bit difficult to understand so let's use real example and see the behavior. We are going to create very simple methods called `foo1` & `foo2`:
```kotlin
@ExperimentalContracts
inline fun foo1(condition: Boolean, block: () -> Unit) {
    contract { callsInPlace(block, InvocationKind.UNKNOWN) }
    if (condition) {
        block()
    }
}

@ExperimentalContracts
inline fun foo2(condition: Boolean, block: () -> Unit) {
    contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
    if (condition) {
        block()
    }
}
```
I added `condition` boolean parameter so that it does not take in account of how many times block is called on runtime. Let's test out `foo1` first:
```kotlin
@OptIn(ExperimentalContracts::class)
fun main(args: Array<String>) {
    val someInput: Int
    foo1(false) {
        someInput = 10
    }
    print(someInput)
}
```
The code itself may not be interesting so let me include the screenshot of IDE:
![Contract on IDE](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z22z3khla2uwjadypft3.png)
![Error message on IDE](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6k21e4qtnb8cw6m1olhf.png)

Because we told the compiler that `block()` parameter is called unknown amount (could not be called), the compiler _thinks_ that `someInput` may not be initialized or it's called many times that `val` cannot be re-assigned to a new value. 

What about `foo2`?
```
@OptIn(ExperimentalContracts::class)
fun main(args: Array<String>) {
    val someInput: Int
    foo2(false) {
        someInput = 10
    }
    print(someInput)
}
```
![Contract with lint fixed on IDE](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yh38em0e2xiuv5ectte3.png)
It looks like `foo2` has no problem even though we know `val someInput` would not be initialized. What happens if we run this method?
```
0
Process finished with exit code 0
```
It appears that the Kotlin compiler might utilize default values to prevent the application from encountering errors or crashing. Typically, this operation might not be feasible, but by using contracts, we can notify the compiler about the state of the called block.

In essence, when the `.apply{}` function is invoked, the compiler explicitly acknowledges that the block has been called and respects whatever operations occur within the caller.

There are many different [contracts](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.contracts/) so check out if you have some free time.

### Function literals with receiver
This is the main part of the .apply{} function. Initially, looking at this function made me scratch my head. However, once we understand extension functions, it becomes really easy to grasp.

Extension functions seamlessly add functionality without requiring modifications to the class.

```kotlin
fun String.printWithCheese() {
    println("$this Cheese. Prefix length ${this.length}")
}

fun main(args: Array<String>) {
    "my".printWithCheese()
}
```
We can add the class with new methods that we want to implement without altering the source code (as shown in this example with the String class).

By using this knowledge of extension functions, we can observe that the block parameter data type in the apply{} function is T.() -> Unit. This data type translates as "Here is the lambda function that has a receiver type, T." It utilizes the same concept as extension functions, allowing access to and modification of member instances.

If we were to look at `.run{}` implementation, we can also observe something similar:
```kotlin
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}
```
The only difference is the return type for this lambda functions, R. `.run{}` uses result from the block and return it as its return type.

## Conclusion
I hope you enjoyed diving into `.apply{}` method and seeing many different components played out in just one simple call. I am amazed by the growth of Kotlin over the years and hope to cover more Kotlin-related topics in the future.