Kotlin logo

Kotlin

A concise multiplatform language developed by JetBrains

Language design

DSLs in Kotlin: Part 1. What’s in the toolbox + Builders

If you have a very nice API, it is the fashion nowadays to call it an internal DSL, because the code that uses such an API reads almost like a language inside your language of choice. Fluent interfaces serve as one of the most popular examples.

Many modern languages provide some advanced means for creating internal DSLs, and Kotlin is no exception here. In this post I will briefly list the features that are useful for this purpose.

Let’s start with extension functions. We all are familiar with Java’s utility classes, like java.util.Collections and like. Such classes are simply containers for a bunch of static methods, which are intended to be used with such and such classes. So we end up writing code like this:

Collection.sort(list);
int index = Collections.binarySearch(list, x);

and this does not look very pretty. Static imports make it prettier, but they don’t solve an important problem of discoverability: we all navigate through APIs with IDE’s code completion capability:
Code completion in IDEA
And wouldn’t it be cool to discover those utility functions the same way? So we have extension functions that are called in the form “a.foo()” even if foo() is not a member of the class of a. For example, those utility functions from Collections could be defined as extension functions, and be called like this:

list.sort();
val index = list.binarySearch(x);

These are still statically dispatched utility functions, i.e. the bytecode emitted by the compiler is the same as in Java, but the syntax is better, and code completion works. Note that, unlike members, extension functions cannot be overridden in subclasses, i.e. some special implementation of List could not override sort() to be more efficient.

To define an extension function, we just put a receiver type in front of its name:

fun <T : Comparable<T>> List<T>.sort() {
  Collections.sort(this)
}

Note that I can use a ‘this’ reference that represents my receiver object. See more here.

Now, what do extension functions give us, DSL creators? First of all you can turn any interface into a fluent one. For example, let’s create a new buffered reader with a given charset:

val reader = FileInputStream("mytext.txt").buffered().reader("utf-8")

Is it a special class I wrote to be able to get this? No. It’s only two functions:

fun InputStream.buffered() = BufferedInputStream(this)
fun InputStream.reader(charset : String) = InputStreamReader(this, charset)

Then, they play very well together with operator overloading: in Kotlin, most operators, such as plus, minus and so on, are compiled by convention to named function calls. For example, when I say “a + b”, Kotlin reads “a.plus(b)” (see more in our docs). This means that by adding an extension function named “plus” to my type I can have a binary ‘+’ working on it. For example, I could make my own ‘+’ for list concatenation:

fun  List.plus(other : List) : List {
  val result = ArrayList(this)
  result.addAll(other)  
  return result
}

And call it like this:

val l1 = list(1, 2, 3)
val l2 = list(4, 5, 6)
val l3 = l1 + l2 // a new list of length 6 is created

And there’s more: since indexation is compiled to calls of get() and set() functions, we can have pretty sublists (or “slices”) that look like this:

val sublist = list[a..b]

By defining an extension function get() on a list:

fun <T> List<T>.get(range : IntRange<Int>) : List<T> 
    = subList(range.start, range.end)

Infix function calls add more on top of that, because you can say, for example

it hasPrivilege WRITE

instead of

it.hasPrivilege(WRITE)

And, of course< you get a whole lot of fun with higher-order functions and function literals (i.e. “closures”). For example, check this out:

lock (myLock) {
  // Do something
}

Is this a built-in construct, like Java’s synchronized section? No, it’s a function call. It uses a very handy convention: you can pass the last function literal outside the parentheses you put around your argument list. So this call is the same as “lock(myLock, {…})”, but looks prettier.

More about this example can be found here.

There’s one other nice convention that makes something very close to LINQ possible:

users
   .filter { it hasPrivilege WRITE }
   .map { it => it.fullName }
   .orderBy { lastName }

The convention is: If a function with only one parameter is expected, the parameter declaration may be omitted, and the default name ‘it’ will be used. I.e. “filter {it.foo()}” is the same as “filter {it => it.foo()}”.

And finally, if we put all this (and just a tiny little bit more) together, we can get something really nice. Look at this code:

html { 
   head { 
     title { +"XML encoding with Kotlin" }
   } 
   body { 
     h1 { +"XML encoding with Kotlin" }
     p { +"this format is now type-safe" }
 
     /* an element with attributes and text content */
     a(href="http://jetbrains.com/kotlin") { +"Kotlin" }
   }
}

Is it Groovy? No, it’s Kotlin, and, unlike Groovy, it’s statically typed. Yes, we can do builders like Groovy, but better. :) I added a detailed explanation of this example to our wiki; you can find it here.

Have a question? Opinion? Suggestion? We really appreciate your comments!

image description