Why contemporary web developers deserve better

Type-safe Web with Kotlin

Can everything in our web application be reusable and statically type checked? Everything, from markup to client scripting. Andrey Breslav tells how it is indeed possible with Kotlin.

You may have heard about Kotlin. It is a modern programming language that compiles to JVM byte codes and JavaScript. It is designed for industrial use, which means maintainability: static typing enables error detection and automated refactoring, type inference makes for clean and readable code and powerful abstractions facilitate library development.

A language isn't much good if you can't use it for the web, so in this article, we'll discuss web programming in Kotlin.

Sure, you can write the same servlets as in Java, and you can use jQuery in Kotlin too, but there’s something else we’ll focus on. But first, let me make a quick detour and tell you a few things about how people make web applications nowadays.

Even a moderate web application involves many technologies, quite a few of which are associated with its own language. It’s usually a minimum of five languages involved:

  • HTML for markup;

  • CSS for styling;

  • Java for server-side business logic (we will limit ourselves to the Java platform in this article);

  • JSP or some other template engine for templating;

  • and, finally, JavaScript for client-side scripting.

So, we have five languages there. If this fact alone makes you a little uncomfortable, read on and we’ll come back to it, but first, let’s have a look at the very core: HTML and CSS.

One very special thing about HTML and CSS is that they are not programming languages, but rather something more like data formats. In particular, neither of these two languages have any means of abstraction or code reuse. (To be fair, we should note that HTML can reuse CSS definitions, but there’s no reuse of CSS inside CSS and no reuse of HTML inside HTML.) I’ll say it again: two of the most popular languages in the world allow no code reuse whatsoever.

The software engineering alarms inside your head should have gone off by this point; does it mean that we copy-and-paste that markup all the time? Exactly.

Many people recognize that this is a problem. Solutions come in the form of better languages. For example, most template engines provide some means of markup reuse (although it’s a pain in many cases). And there are LESS and SASS that bring to CSS such bleeding-edge features like named constants for example.

Let me explain. If you are using plain CSS, every time you describe, say, a color, Cantaloupe, for example, you enter a literal constant #ffcc66 (or #fc6). Perhaps you have 36 occurrences of this constant all over your CSS. Now, you want to change some of them to something else and you’re not liking your job so much this afternoon.

Obviously the right way to handle this is to define named constants corresponding to the roles of that color, so that the value needs to be changed only once per role. A named constant abstracts over a value and allows for code reuse. CSS does not have named constants. We have to use some preprocessor like LESS to properly reuse our CSS code.

There are whole websites that generate CSS for you. You say something like “I want a button of that shape and that color,” and get a CSS snippet. So far so good. But then you copy-and-paste that snippet to your stylesheet. There’s no library that defines a component named Button with well-typed properties shape and color, CSS simply does not support that. Instead, there’s a bunch of text fields somewhere on the web that does what a library should do: provides a higher level of abstraction. But importantly, it can’t provide code reuse.

To summarize: web programming lacks means of abstraction and code reuse.

On top of that, when your application gets bigger (and this is when development costs start to matter), other problems come into play: the only way to catch bugs in web applications is testing. Even very stupid bugs, like misspelling a name.

Don’t get me wrong: one has to test, there’s no way around it. But there are somewhat trivial safety properties that show up all over the place, and writing tests for all of them is just too much work. Having to test for trivial things a lot is too expensive.

Trivial errors should be detected automatically by the compiler. Yes, I’m talking about static typing. Dynamic languages are cool, but I also know that modern static languages are even cooler, as they give you nearly the same flexibility, but in addition they give you safety.

In a static language you can have automated refactorings, accurate code completion and other IDE features that save you time and effort en masse.

You may have seen it coming: Kotlin is a statically typed language that has very good means of abstraction and code reuse, so why don’t we use Kotlin instead of those five languages mentioned above?

The rest of this article will describe how this can be done. We will focus on principles and not any particular implementation, but I’ll give you a reference so that you have something to get your hands on. Kara is an open source web framework written entirely in Kotlin. It unifies many aspects of a web application in the form of DSL-like APIs, so that every bit of your markup or any other code is statically checked and can be reused. Again, this article is not about this particular framework, but I’ll occasionally use examples from Kara to illustrate the principles under discussion.

Birds-eye view

First, let’s see what it means to “unify many aspects of a web application in the form of an API”. Listing 1 shows what this mark up looks like in Kara.

Listing 1: Kara mark up

 

html {
  head {
    title(“Example”)
  }
  body {
    ul {
      li { +“Item 1” }
      li { +“Item 2” }
    }
  }
}

 

Ends 

This code creates a tree of tags that can be serialized to textual form upon request. You can see it is almost HTML, only with curly brackets instead of angle ones (and somewhat fewer of them). In fact, this is just a bunch of function calls, absolutely normal code written in Kotlin. It is called a type-safe builder, very much like builders Groovy is famous for, but statically checked. If you, for example, put a li outside ul, you’ll have a compilation error:

 

body {
  li { ... } // Compilation error here
}

 

 

Builders can handle everything your templates do. Firstly, they can generate arbitrarily complex HTML by using loops. For example, to generate a list of colors, we can write:

 

 ul {
     for (color in listOf(“red”, “green”, “blue”)) {
          li { +color }
     }
 }

 

 

You can also reuse parts of your markup by simply extracting code into functions, for example a function that renders a list of strings, as shown in Listing 2:

Listing 2

 

fun BodyTag.list(vararg items: String) {

   ul {

       for (x in items)

           li { +x }

   }

}

 

body {

   list(“red”, “green”, “blue”)

   list(“cyan”, “magenta”, “yellow”)

}

 

Ends

We’ll get to how it works later, but now let’s see what a stylesheet looks like.

 

ul {
  listStyleType = circle
  margin = 10.px
}

 

 

This even looks very much like CSS, but again, it is type-safe meaning we cannot use an undefined attribute or put a wrong value in. What’s even more important is that it is code, which means that we can use arbitrarily complex arithmetic and/or named constants.

 

val BASE_MARGIN = 5.px // named constant

ul {
  listStyleType = circle
  margin = BASE_MARGIN * 2 // arithmetic
}

 

 

It also means that we can extract a part of this code into a function, as shown in Listing 3.

Listing 3 

 

// Define a function
fun StyledElement.doubleMargin() {
  margin = BASE_MARGIN * 2
}

ul {
  listStyleType = circle
  doubleMargin() // use the function
}

 

Ends

So, we clearly get reuse and type-safety. Now, let’s start digging to understand how all this works.

Inside a Builder 

Now we're ready to see how type-safe builders work. Let me first remind you that it is not a built-in language construct, but rather a normal Kotlin API. To make this API look like a declarative DSL we need two things: lambdas and extension functions.

Lambdas 

Lambdas are sometimes called anonymous functions. In other words, they are expressions whose values are functions. To filter a list, you need to pass in a predicate, i.e. a piece of code that tells the filter() function which elements to keep and which to remove:

users.filter({u -> u.age >= 21})

In Kotlin, lambdas are delimited by curly braces; inside a lambda an arrow (“->”) separates a list of parameter names from the body. In the example above the lambda expression {u -> u.age >= 21} corresponds to a function that takes one parameter named u (its type is inferred from the context) and returns a boolean result of comparison. This function is passed as a parameter to the filter() function.

The filter() function is good at explaining what lambdas are for, but is too complex to be the first example to explain, so we will take a simple function now: 

let(1 + 2, { x -> println(x * x) })

The let() function is very simple: it takes its first argument and passes it to the second argument (which is a lambda). The example above prints “9”. There is a nice syntactic rule that allows you to write the last lambda outside of the parentheses:

let(1 + 2) {
    x -> println(x * x)
}

This code is doing exactly the same as the previous example, but the lambda now looks more like a body of some “language construct”. We borrowed this syntactic convention from Groovy where it works very well. Now let’s look at how let() is defined. First, a simple version that works only with numbers:

fun let(x: Int, f: (Int) -> Unit) {
    f(x)
}

The body of let is rather trivial: simply apply f() to x. Let’s take a close look at the declaration of f: 

f: (Int) -> Unit

Since this is a parameter whose value is a function, it has a special kind of type, a function type. Function types have the form of (parameter types) -> return type, so f() takes one parameter of type Int and returns a value of type Unit (which means “no interesting value”, very much like void). This is why we can call it as f(x) inside let() and this is why we can pass a lambda as its value.

Now, let’s make our function more useful and allow any type T, not only Int, for parameter: 

fun <T> let(x: T, f: (T) -> Unit) { 
    f(x) 
}

We simply declared a generic parameter T and used it instead of Int. No big deal. Functions like let(), that take other functions as an arguments, are called higher-order functions, and they are very important as means of abstraction. Now, we move on to our next subject: extension functions.

Extension functions

In mainstream languages, extension functions were introduced by C#. Their essential purpose is to extend an API of a class or interface, without it having to change (and thus, own) its class or interface. For example, it means that you can create an extension function that returns the last character of a string:

fun String.last(): Char {
    return this.charAt(this.size - 1)
}

This function is defined somewhere outside the String class, and it does not change the class in any way, but the language allows you to call it as if it were a member:

println(“abc”.last()) // prints “c” 

In fact, you are looking at a simple static utility method, like dozens of those you have in your Java projects: 

public class StringUtil {
    public static char last(String s) {
        return s.charAt(s.length() - 1)
    }
}

Out last() defined above is compiled to exactly this, but the syntax is nicer and it enables discovery by code completion. In Kotlin, an extension function shows up when you hit Ctrl+Space after a dot.

But let’s take a closer look: what turns a function into an extension is the “String.” in front of its name? This means that this function can be called on strings. To access the string it is called on, we can use “this” keyword, as if we were inside the class (but no privileges like access to private members). Of course, “this” can be omitted (and it is important for builders):

fun String.last(): Char {
    return charAt(size - 1)
}

Now we have the two components, lambdas and extensions ready. Let’s mix them to get a builder.

The mix

To approach it gently, we’ll start by slightly changing our let() function from above by calling the new version “with”. Let’s turn let()’s parameter f into an extension:

fun <T> with(x: T, f: T.() -> Unit) {
    x.f()
}

The change is very small. We write “T.() -> Unit” instead of “(T) -> Unit”, and f() turns into an extension function, so that we can call it as “x.f()”. Now let’s look at how we can use this new function: 

with(StringBuilder()) { 
    this.append(“Hello”) 
}

We then need to create a new StringBuilder object (there’s no new operator in Kotlin) and pass it to with(). The lambda sees this StringBuilder under the name “this” and calls its append(). Of course, “this” can be omitted: 

with(StringBuilder()) { 
    append(“Hello”) 
}

Now you can see that the lambda (the “body” of with()) has a different context: it can call methods of StringBuilder() without explicitly mentioning the receiver. Arbitrary constructs may be used as well.

with(StringBuilder()) {
    append(“List:\n”)
    for (x in list) {
        append(x)
        append(“\n”)
    }
}

This is almost a builder already as we have all the components. Now, let’s use them and create a real builder for a tiny subset of HTML.

HTML Builder

The key thing with builders is tracking context. As we have seen in the with() function, the context can be tracked by using extension lambdas. So, with(x) {...} makes x the context of whatever comes inside the curly braces. Let’s use the same principle to build a tree of HTML tags.

For simplicity we will use only three tags: body, ul and il, and text elements (representing free text inside an HTML page). First, we’ll have an abstract class Element representing any element of an HTML document (a tag or text): 

abstract class Element {
    val children = ArrayList<Element>()
}

All it does is holds its children. Now, Body and Text are derived from it, as shown in Listing 4

Listing 4

 

open class Body: Element() {
    fun text(s: String) {
        children.add(Text(s))
    }

    ...
}

class Text(val text: String): Element()


 

Ends

We are now ready to create a root function for our builder, the one that creates a root tag. And in our case it is body(): 

fun body(init: Body.() -> Unit): Body {
    val body = Body()
    body.init()
    return body
}

Calling this function is analogous to calling “with(Body()) {...}”, but it’s shorter and cleaner: 

body {
    text(“Hello”) // adds a new Text element to body’s children list
    text(“world”) // adds another Text element to the children list
}

Now, let’s add the UL tag:

class UL: Element() {
    ...
}

UL should be available inside Body, so we need to add the corresponding builder function to the Body class, as shown in Listing 5:

Listing 5

 

open class Body: Element() {
    ...

    fun ul(init: UL.() -> Unit) {
        val ul = UL()
        children.add(ul)
        ul.init()
   }
}

 

Ends

The ul() function is analogous to body(), with one difference: it does not return a new tag element, but rather adds it to the children list of Body: 

body {
    text(“List:”)
    ul {
        // a new instance of class UL is added to body.children
    }
}

In the same manner we can have LI inside UL like in Listing 6:

Listing 6

 

class UL: Element() {
    fun li(init: LI.() -> Unit) {
        val li = LI()
        children.add(li)
        li.init()
    }
}

class LI: Body()

 

Ends

The pattern is the same: create a new LI, initialize it with the lambda passed to li() function, and add to the children list of UL. Note that LI extends Body and hence gets all of its members: you can create text elements and lists inside LI (but not directly inside UL):

body {
    text(“List:”)
    ul {
        li { text(“First”) }
        li { text(“Second”) }
    }
}

Last question: how does Kotlin know that li() calls correspond to UL, while ul() corresponds to body()? This is crucial for our builder, because we want elements added to appropriate children lists and form a proper tree. The answer is roughly that it looks for the innermost “this” that is applicable, so when you say li(), the compiler looks up the syntax tree and finds the nearest “this” that has a li(). If there’s no such “this”, i.e. you are trying to add a LI directly to Body, the compiler reports an error. 

Add a few hundred tags and you have a complete DSL for HTML templates. In fact, it is rather easy to generate all those tag classes once and for all, and this is one of the things done by creators of Kara. The CSS is done analogously.

Now let’s revisit the reuse examples that were left unexplained in the first section in Listing 7:

Listing 7

fun Body.list(vararg items: String) {
    ul {
        for (x in items)
            li { +x }
    }
}

body {
    list(“red”, “green”, “blue”)
    list(“cyan”, “magenta”, “yellow”)

Ends

The trick is, again, that an extension function tracks the context for you: when you call list(), the compiler looks for the nearest “this” that has such a member, or if there’s no member, such an extension. When list() is invoked in this example it takes the current Body instance as its receiver parameter, and passes it to the ul() call.

Summary

Contemporary web programming lacks means of reuse and abstraction as well as type safety. Contemporary web developers deserve better. The situation can be improved by using a modern programming language such as Kotlin and leveraging its type system and abstractions. We demonstrated this approach by creating a type-safe builder DSL inside Kotlin to handle HTML generation, outlined how other aspects of a web application can be addressed in similar ways. A full-scale implementation of the concepts presented above is being created in the Kara project.

Examples used in this article are available at https://github.com/abreslav/kara-files/blob/master/src/tutorial.kt and https://github.com/abreslav/kara-files/blob/master/src/small_builder.kt

Author Bio: Andrey is the lead language designer working on Project Kotlin at JetBrains. He also works on making the Java language better, serving as a Java Community Process expert in a group for JSR-335 "Project Lambda". In what spare time is left he tries to make sure that his traveling is not all about work and teaches programming to high-school children.

This article appeared in JAX Magazine: On Cloud Nine. You can read more of that issue and others here

Andrey Breslav
Andrey Breslav

What do you think?

JAX Magazine - 2014 - 05 Exclucively for iPad users JAX Magazine on Android

Comments

Latest opinions