Function Types with Receivers in Kotlin - The Power Behind Kotlin's apply, with, and run Scope Functions
Tags: kotlin, functions
Published: 2023-10-18 05:40:05

---
title: Function Types with Receivers in Kotlin - The Power Behind Kotlin's apply, with, and run Scope Functions
published: true
date: 2023-10-18 04:12:30 UTC
tags: kotlin, functions
canonical_url: https://blog.nicholasfragiskatos.dev/function-types-with-receivers
---

When we invoke one of Kotlin's _Scope_ functions (`apply`, `run`, `let`, `also`, or `with`) a temporary scope/context is created for the object, and you can access the object without its name. Depending on the function you can reference the context object using either `it`, or implicitly with `this`.

The `apply`, `run`, and `with` functions provide the context object with `this`, and most interestingly, as a result, they let you define your function block as if it were a native member of the object's type.

For example, consider the following:

```kotlin
val myList = mutableListOf("Ron", "Leslie", "Ben", "Ann")  

myList.apply {  
    add("Chris")  
    remove("Ron")  
}

println("[${myList.joinToString(", ")}]")

// Output: ["Leslie", "Ben", "Ann", "Chris"]

```

`add` and `remove` are both functions as defined by the `MutableList` interface, but because of the lambda's implicit `this` context, we can reference them without any qualifiers. Unlike if using `let`, for example. However, even with `let`, we can achieve the same outcome:

```kotlin
val myList = mutableListOf("Ron", "Leslie", "Ben", "Ann") 

myList.let {  
    it.add("Chris")  
    it.remove("Ron")  
}

println("[${myList.joinToString(", ")}]")

// Output: ["Leslie", "Ben", "Ann", "Chris"]

```

We can take a look at the definition of the `apply` function and see it's very short. The real functionality that we are interested in lies in the function's argument, and the last two lines.

```kotlin
@kotlin.internal.InlineOnly  
public inline fun <T> T.apply(block: T.() -> Unit): T {  
    contract {  
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)  
    }  
    block()  
    return this  
}

```

💡

>We can disregard the`contract{}`block. That's a Kotlin feature that allows the developer to share some extra metadata with the compiler.[Here is a good blog post about it.](https://blog.kotlin-academy.com/understanding-kotlin-contracts-f255ded41ef2?gi=922710c454a4) For this discussion about Function Types with Receivers, it's not necessary.

From just naively using the `apply` function, we know that it runs some block of code that we pass to it and that block of code is within the `this` context, and lastly the `apply` function returns the context object itself as a result.

That's exactly what we see happening in the last two lines of the function. There's an invocation of `block`, which is the functional parameter passed by the caller, and then finally it just returns `this`.

However, `block` doesn't seem to be a regular functional parameter. We notice that its type is a little different. It looks more like an [Extension Function](https://kotlinlang.org/docs/extensions.html#extension-functions)

```kotlin
block: T.() -> Unit

```

The parameter is similar but not quite an extension function. It is instead a Function Type with a Receiver. Let's dive deeper into what that means by first starting with breaking this feature down into its two key parts, function types and receivers.

## Function Types

Just like variables, functions in Kotlin can also have a type. A function type uses special notation that defines the function's arguments and return type. Function types have the following general template:

`(A, B, C,...) -> Z`

This is a type that represents a function with arguments of types A, B, C, etc. and returns a value of type Z.

Some concrete examples:

- `() -> Unit`, a function that takes no arguments, and returns nothing.

- `(String) -> Int`, a function that takes a `String` argument and returns an `Int`.

- `((Char) -> Unit) -> Unit`, a higher-order function that takes a function as an argument and returns nothing.

Kotlin, like many other programming languages, treats functions as first-class citizens. This means you can use a function in the same ways you would use any other type: assigning functions to variables, passing them as arguments to other functions (higher-order functions), and even returning a function as a result of another function.

Kotlin provides us with a good bit of freedom with how we can define an implementation of a function type. No matter which way it is defined it can still be used in a higher-order function and it will provide the same results.

As an example, let's take a look at the `filter` function that we have access to when working with a `Collection`. When we call `filter` all we must do is make sure to provide it with a function type that accepts an argument of type T and returns a boolean.

```kotlin
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {  
    return filterTo(ArrayList<T>(), predicate)  
}

```

Here are the different ways we can satisfy this functional argument when invoking `filter`.

### Lambda Functions

Lambda functions allow the developer to quickly and conveniently define an implementation for a functional argument when invoking a higher-order function without first having to define it elsewhere. This is probably the most common way of invoking functions with a functional argument.

```kotlin
val myList = mutableListOf("Ron", "Leslie", "Ben", "Anne", "Chris")

val filterByLambda: List<String> = myList.filter { str: String ->  
    str.isNotBlank() && str.length > 4  
} 

println("filterByLambda: [${filterByLambda.joinToString(", ")}]")

// Output: 
// filterByLambda: ["Leslie", "Chris"]

```

### Anonymous Functions

Anonymous functions are similar to lambda functions but with different syntax.

```kotlin
val myList = mutableListOf("Ron", "Leslie", "Ben", "Anne", "Chris")

val filterByAnonymous: List<String> = myList.filter(fun(str): Boolean {  
    return str.isNotBlank() && str.length > 4  
})  

println("filterByAnonymous: [${filterByAnonymous.joinToString(", ")}]")

// Output: 
// filterByAnonymous: ["Leslie", "Chris"]

```

### Function Variables

We can also store a function literal inside a variable with a matching function type. Then you just pass that variable to the function like you would any other argument.

```kotlin
val myList = mutableListOf("Ron", "Leslie", "Ben", "Anne", "Chris")

val myFilterFunctionVariable: (String) -> Boolean = { str: String ->  
    str.isNotBlank() && str.length > 4  
}  

val filterByFunctionVariable: List<String> = myList.filter(myFilterFunctionVariable)  

println("filterByFunctionVariable: [${filterByFunctionVariable.joinToString(", ")}]")

// Output: 
// filterByFunctionVariable: ["Leslie", "Chris"]

```

### Function Declarations to Function Types

Lastly, we can take an existing, traditional declaration of a function and use the member reference operator (`::`) to create an instance of a function type from the declaration.

```kotlin
val myList = mutableListOf("Ron", "Leslie", "Ben", "Anne", "Chris")

fun myFilterPredicate(str: String) : Boolean {  
    return str.isNotBlank() && str.length > 4  
}

val filterByNamedFunction: List<String> = myList.filter(::myFilterPredicate)  

println("filterByNamedFunction: [${filterByNamedFunction.joinToString(", ")}]")

// Output: 
// filterByNamedFunction: ["Leslie", "Chris"]

```

## Receivers

Receivers are something we use all the time, but we just don't think about it. Essentially, any time you are using dot notation to reference some member function for a specific instance of an object, you are using a receiver. For example, if we have `myObj.someFunction()`, `myObj` is considered the _receiver_ of the function call `someFunction`.

Each receiver has its own context and within that context `this` references that specific object instance. Furthermore, inside a class, any reference to a member property or function always has an implicit `this` attached to it. Although, for syntax convenience, we never have to type it.

Now consider the following:

```kotlin
class MyCustomObject(val id: String) {

    fun getFormattedId() : String {
        return "MyCustomObject-${id}" // (implicit this reference for id)
        // or equivalently
        // return "MyCustomObject-${this.id}" (explicit this reference)
    }
}

val pawnee = MyCustomObject("Pawnee")
val eagleton = MyCustomObject("Eagleton")

println(pawnee.getFormattedId()) // prints MyCustomObject-Pawnee
println(eagleton.getFormattedId()) // prints MyCustomObject-Eagleton

```

When we have `pawnee.getFormattedId()`, the `pawnee` instance variable is considered the receiver of the function call `getFormattedId` and `this.id` points to "_Pawnee_". Likewise, when we have `eagleton.getFormattedId()`, the `eagleton` instance variable is now the receiver of that function call and `this.id` points to "_Eagleton_". This is how the `getFormattedId` function knows which `id` to get.

## Function Types with Receivers

As we discussed earlier, `(A) -> B` denotes a function type for a function that has one argument of type `A` and has a return value of type `B`. Optionally, a function type can also have a receiver type, which is defined with the following form:

`R.(A) -> B`

This represents a function that has one argument of type `A` and has a return value of type `B`, as before, but additionally is called on a receiver type `R`.

Here is an example of a function that has a receiver with type `String`, an argument with type `String`, and that returns a type `Boolean`:

```kotlin
val isLonger : String.(String) -> Boolean = { other: String ->  
    this.length > other.length  
}

```

We can then invoke this function by prepending a receiver to the function, similarly to how we invoke any other object's function:

```kotlin
val result1_a: Boolean = "Leslie".isLonger("Anne")
val ben = "Ben"
val result2_a: Boolean = ben.isLonger("Chris")

println("Is Longer, Result1: $result1_a") // true  
println("Is Longer, Result2: $result2_a") // false

```

We can also invoke a function type with a receiver _without_ prepending the receiver, but instead, pass the receiver as the first argument to the function:

```kotlin
val ben = "Ben"
val result1_b = isLonger("Leslie", "Anne")  
val result2_b: Boolean = isLonger(ben, "Chris")  

println("Is Longer, Result1_b: $result1_b") // true
println("Is Longer, Result2_b: $result2_b") // false

val result1_c = isLonger.invoke("Leslie", "Anne")  
val result2_c: Boolean = isLonger.invoke(ben, "Chris")  

println("Is Longer, Result1_c: $result1_c") // true
println("Is Longer, Result2_c: $result2_c") // false

```

## The Comparison to Extension Functions

Extension functions are full static function declarations with one implementation. When we create an extension function, the class isn't modified but instead the actual code compiles to a static function declaration. For example, when we define an extension function like:

```kotlin
fun String.countNumberOfVowels() : Int {  
    val vowels: Set<Char> = setOf('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U')  
    var count = 0  

    for (char in this) {  
        if (char in vowels) {  
            ++count  
        }  
    } return count  
}

val vowels = "Hello, World".countNumberOfVowels()

```

The extension function definition and usage are syntactic sugar, and what the extension function ultimately gets resolved to is:

```kotlin
fun countNumberOfVowels(word: String) : Int {  
    val vowels: Set<Char> = setOf('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U')  
    var count = 0  

    for (char in word) {  
        if (char in vowels) {  
            ++count  
        }  
    } return count  
}

val vowels = countNumberOfVowels("Hello, World")

```

In fact, if you try and define both of these functions in the same package you will receive a build error:

> Kotlin: Platform declaration clash: The following declarations have the same JVM signature (countNumberOfVowels(Ljava/lang/String;)I):
> 
> fun countNumberOfVowels(word: String): Int defined in root package  
> fun String.countNumberOfVowels(): Int defined in root package

### Similarities

Extension functions and function types with receivers are the same in that they are largely just syntactic sugar, and they both achieve the same thing:

1. Extending the functionality of a class without actually modifying the class.

2. Providing a `this` scope within the function literal so the user can implement the function literal as if it were a native member of the class.

### Differences

- Extension functions are function declarations, and as the name implies, function types with receivers are just types, like any other type. In the case of higher-order functions, the type's implementation only gets defined when called.

- We saw earlier that a function type with a receiver can be invoked with _and_ without specifying the receiver.

- Extension functions are a way to avoid having to resort to either inheritance, the decorator design pattern, or defining and using a bunch of util classes when extending a class' functionality. Function types with receivers are more concerned about making life easier when working with higher-order functions.

## Conclusion

_Function types with receivers_ are a powerful feature and a key part of how Kotlin's `apply`, `with`, and `run` scope functions work. It's a feature that allows for greater convenience and code clarity when developing in Kotlin.

To fully understand this feature, we dove deeper into smaller concepts like _function types_ (learning how to define and use them), as well as _receivers_ (what they are and how they fit together with function types). Lastly, we looked at how a _function type with a receiver_ compares to and contrasts with an _extension function_.

* * *

If you noticed anything in the article that is incorrect or isn't clear, please let me know. I always appreciate the feedback.