Blog Infos
Author
Published
Topics
,
Published
Topics
,
Kotlin Function Literals with Receiver — The basis for DSLs and many Library Functions

As we know, Kotlin makes heavy use of functions that take other functions as their argument. This is one of two types of functions we call higher-order function. Related to this, Kotlin also comes with first-class support for passing functions around using function literals. There are two types of function literals: lambdas and anonymous functions. The entire standard library wouldn’t be half as powerful if it wasn’t using any higher-order functions.

Typical examples of higher-order functions in Kotlin are candidates such as mapfiler or fold as can be used for collections.

On top of that, there’s a special type of higher-order function that adds a very important tool to the language: Function literals passed to other functions can work with a so called receiver to improve both the call and the definition side. In this article, I will be explaining how to identify, write and use those function literals in your code. A popular example of this kind of function is used with the apply scope function that is shown in the following example:

val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)
view raw kotlin-apply.kt hosted with ❤ by GitHub

Kotlin apply function example

 

Isn’t it interesting that age can be accessed without naming the object as in person.age? How is this structure made possible?

The entire concept of function literals with receiver is what makes Kotlin a great choice for designing Domain Specific Languages.

Same basics

Kotlin, other than Java, has proper function types, which means that variables can represent a type such as a function accepting an integer and returning a string:

(Int) -> String // a function type

We can use those function types as parameters for other functions. We call those functions “higher-order functions”.

// A function `higherOrderFunction` taking another function `functionAsArgument` as its argument.
// The function calls the passed in function passing the value `5` to it. The result of `functionAsArgument(5)`
// is returned back to the caller.
fun higherOrderFunction(functionAsArgument: (Int) -> String) =
functionAsArgument(5)
view raw kotlin-hof.kt hosted with ❤ by GitHub

Definition of a higher order function

 

To call the depicted function as a client, we pass a lambda, sometimes also referred to as function literal, to the function:

// we pass a lambda to higherOrderFunction which needs to be of type `(Int) -> String` as defined
// in the function definition above.
higherOrderFunction { "The Number is $it" }

Calling a higher-order function

 

Next level: Function Literals with Receiver

As seen in the previous part, function literals are used as arguments to other functions, which is already an awesome feature by itself.

Kotlin goes one step further and provides support for a concept called function literals with receivers. This feature enables the developer to call methods on the receiver of the function literal in its body without any specific qualifiers. This is quite similar to extension functions, as they also make it possible to access members of the extension’s receiver object inside the extension code. Let’s see what these function literals look like:

// Defines a variable named `greet` of type `String.() -> Unit`.
// This is a function literal with receiver type in which the receiver is of type `String`
var greet: String.() -> Unit = { println("Hello $this, your name contains $length letters.") }
view raw flwr-def.kt hosted with ❤ by GitHub

Definition of a function literal with receiver type

Job Offers

Job Offers


    Sr. Software Development Engineer, Last Mile Driver Assistance Technology

    Amazon
    Berlin
    • Full Time
    apply now

    Kotlin Multiplatform Mobile Developer

    Touchlab
    Remote
    • Full Time
    apply now

OUR VIDEO RECOMMENDATION

, ,

Treasure.map(): Functional Programming in Kotlin

What is a monad? Do I need a PhD to write in functional style? Any hidden gems that will improve my developer experience? In this talk, we’ll cover the core concepts of functional programming, discuss…
Watch Video

Treasure.map(): Functional Programming in Kotlin

KARIN-ALEKSANDRA MONOID
Senior Software Engineer

Treasure.map(): Functional Programming in Kotlin

KARIN-ALEKSANDRA M ...
Senior Software Engi ...

Treasure.map(): Functional Programming in Kotlin

KARIN-ALEKSANDRA ...
Senior Software Engineer

Jobs

We define a variable of type String.() -> Unit that represents a function type () -> Unit with String as the receiver. All methods of this receiver can be accessed in the method body without the use of an additional qualifier. If we need to refer to the receiver explicitly, we do so using the this as shown in the example. The caller has two possible ways to invocate this function:

// like a regular function
greet("hello")
// like an extension function on the string
"hello".greet()
view raw flwr-call.kt hosted with ❤ by GitHub

calling a function literal with receiver type

 

With these basics in mind, let’s go through an example.

Scope functions in action

As already mentioned in the beginning of this article, Kotlin’s standard library contains multiple scope functions, one of which is apply. It is defined as shown here:

public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
view raw apply-def.kt hosted with ❤ by GitHub

Apply function definition

 

The apply function is defined as an extension function to all types, denoted by the generic type T, and it expects a function literal with a generic receiver of the very same generic type T. The implementation is pretty straightforward: the function literal argument is called before the receiver of applyis returned to the caller. The apply function, though looking very simple, is extremely powerful. One of the things you can do with it is the initialization of objects as shown here:

val bar = Bar().apply {
foo1 = Color.RED
foo2 = "Foo"
}
view raw apply-in-use.kt hosted with ❤ by GitHub

Apply function in action

 

In this, an object of type Bar is created and apply called on it. The new object becomes the receiver of apply. At the same time, the lambda passed to apply works on the same receiver which results in unqualified access to foo1 and foo2 which are both properties of type Bar.

If the function parameter taken by apply didn’t define a receiver, we’d have to qualify access to the Bar object using it.foo1 (it being the implicit lambda argument name which can also be changed into an arbitrary name). Thanks to function literals with receiver types, this becomes more easy.

It’s important to be aware of this structure because it’s essential when trying to understand more complicated constructs in Kotlin.

A quick detour to DSLs

As promoted earlier in this article, the concept of function literals with receiver is the foundation for more complicated structures such as Domain Specific Languages (DSLs). Here’s a short example of what this looks like:

class HTML {
fun body() { ... }
}
fun html(init: HTML.() -> Unit): HTML {
val html = HTML() // create the receiver object
html.init() // pass the receiver object to the lambda
return html
}
// Call `html` and pass lambda to it. Inside the lambda we can call `body` directly thanks to the usage of a receiver.
html { // lambda with receiver begins here
body() // calling a method on the receiver object
}
view raw dsl.kt hosted with ❤ by GitHub

Kotlin DSL example

 

If you want to find out more about DSLs, have a look at the official documentation here.

Enjoy!

This article was originally published on proandroiddev.com on September 08, 2022

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
Hi, today I come to you with a quick tip on how to update…
READ MORE
blog
Automation is a key point of Software Testing once it make possible to reproduce…
READ MORE
blog
Drag and Drop reordering in Recyclerview can be achieved with ItemTouchHelper (checkout implementation reference).…
READ MORE

Leave a Reply

Your email address will not be published.

Fill out this field
Fill out this field
Please enter a valid email address.

Menu