Idiomatic Kotlin: Lambdas with Receiver and DSL

Tompee Balauag
Familiar Android
Published in
5 min readJul 12, 2018
“A flatlay with a vintage typewriter, pieces of wooden type, a cup of coffee and red roses” by rawpixel on Unsplash

This article is a part of the Idiomatic Kotlin series. The complete list is at the bottom of the article.

In this article, we will try to explore a new lambda feature and pave the way to Internal DSLs and how Kotlin supports it. As a prerequisite, I encourage you to read the article first regarding lambdas.

What is a lambda with a receiver?

A lambda with a receiver allows you to call methods of an object in the body of a lambda without any qualifiers. It is similar to the typed extension function but this time, for function types. The idea is similar to object initializers in C# but is extended to functions and in a declarative way.

Motivation

Aside from syntactic sugar and conciseness, lambdas with receivers allows you to use expressive APIs that are suited for internal DSLs. They are good for this purpose because DSLs are structured languages and lambda with receivers easily provide this ability to structure APIs and the ability to represent nested structures.

Lambda with receiver in depth

We have briefly tackled the syntax of a function with a lambda as the last parameter in the Lambdas article but for the benefit of those who are not yet familiar, we will define yet another example.

The function accepts a StringBuilder instance, an attibute name and a lambda expression as a last parameter. Recall that when a lambda expression is at the end of the parameter list, you can take it out of the parentheses during invocation.

This invocation method improves readability in my opinion. It makes the intention of the function clearer. Running the piece of code above will output

<attr>MyAttribute</attr>

There is nothing special going on yet. The function accepts an instance of StringBuilder, then the action operates on this instance. Notice that since the function type of our lambda is (StringBuilder) -> Unit, this will be interpreted as a function with a single parameter. Remember that Kotlin can generate a argument name called it for single argument function types. And we need to use this it or your custom parameter name explicitly inside the lambda expression to express our intention that we are going to operate on that object.

As a build up for extension function types and receivers, let us recall about extension functions (this is also discussed previously here). We can declare an extension function of a type by using the Type.method syntax to signify that we will be receiving an instance of that type as the first parameter. What if I tell you we can use this this concept on lambdas as well?

Let us refactor the code above and use the extension function syntax that we discussed above.

We changed

(StringBuilder) -> Unit

to

StringBuilder.() -> Unit

to signify that this action that we are going to receive is an extension function of type StringBuilder. Therefore, we can invoke this action using

sb.action()

by virtue of extension functions. The main takeaway with this is that the action will be receiving an instance of the StringBuilder automatically. In the lambda definition site, you can now reference this receiver object by using this or omit it at all.

The refactored code is an example of a lambda with a receiver. It uses extension function types to simplify method invocation at the lambda definition site. Now let’s take a look at 2 of the most common lambdas with receivers in the Kotlin standard library.

The with function accepts a receiver instance and a lambda extension function of that instance type. This is similar to our example. This is mainly used to perform operations on the object and return another object type. apply on the other hand is an extension function of a generic type. You can use this to operate on the receiver object and will return the same object.

Domain-Specific Language

In contrast with general purpose programming language (such as Kotlin), which can solve potentially all computer problems, domain-specific languages or DSLs are languages that specializes in solving a specific problem domain and can be very efficient at it. Since DSLs are catered to a specific application domain, their syntax and structure are more straightforward than general programming languages. Examples of systems that uses DSLs are Gradle and SQL-based databases.

There are 2 types of DSLs, internal and external. External DSLs are DSLs with their own syntax and requires work when integrating to an application written with a general programming language. Internal DSLs on the other hand is written in the syntax of a general programming language but still follows the rules of the underlying DSL syntax. Think of it as wrapping the external DSL in your favorite programming language.

Creating DSLs using Lambdas with Receivers

To demonstrate internal DSLs in Kotlin using lambda with receivers, let us create a simple android view layout generator. A static android view layout is written in XML and follows the rules of XML syntax. We will try to create an XML generator whose syntax closely resembles XML element hierarchy, something like this.

XML.child
.attribute
.attribute
.child
.atttibute
.attribute
.child
.atttibute
.attribute

First let us create an XMLContainer class. This will represent our outermost container.

Our container has only 1 function named child that accepts a lambda with a receiver of type XMLChildContainer. This means that our outermost container can only have children of type XMLChildContainer, nothing more. This is one of the benefits of internal DSLs. You can enforce specific rules to verify the correctness of the output.

The child function accepts a tag as the name of the element, and a lambda that when evaluated will contain the data of the child. Now let’s look at the code of the XMLChildContainer.

The XMLChildContainer is also a container but with added attributes. Hence the 2 methods attr and child from its super class. Now lets create put the generator to the test.

The above code closely approximates our target syntax of generating android layout. Running this code will output this (formatted for readability)

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"></ImageView>

<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"></TextView>
</LinearLayout>

Sweet! Be warned though that this is not a robust and full-fledged android view layout generator. It was just presented for the purpose of demonstrating internal DSLs.

At this point, you can now create DSLs in Kotlin, in an idiomatic way.

Check out the other articles in the idiomatic kotlin series. The sample source code for each article can be found here in Github.

  1. Extension Functions
  2. Sealed Classes
  3. Infix Functions
  4. Class Delegation
  5. Local functions
  6. Object and Singleton
  7. Sequences
  8. Lambdas and SAM constructors
  9. Lambdas with Receiver and DSL
  10. Elvis operator
  11. Property Delegates and Lazy
  12. Higher-order functions and Function Types
  13. Inline functions
  14. Lambdas and Control Flows
  15. Reified Parameters
  16. Noinline and Crossinline
  17. Variance
  18. Annotations and Reflection
  19. Annotation Processor and Code Generation

--

--