Share this post

Here at Facile.it we are constantly dealing with a lot of forms: we use them to collect various information and data needed to feed our in-house comparing algorithms. These forms could be really complex, having multiple rules and dependencies between fields, and they are likely to be changed and tuned frequently.

When I joined Facile.it, a lot of forms within the Android app needed to be updated or modified and sticking with the existing strategy would have required me to do a lot of work just to add or remove a simple field. So I took a step back and I started thinking about a strategy that would have allowed me to define and structure a form in a more flexible and declarative way. I wanted to be able to declare the relationships between fields, their validation rules and their serialized representation (how they are sent to the server).

I firstly thought about defining it using some configuration file, maybe written in JSON or YAML. The problem with this strategy was that it would also have required me to write a lot code to parse and validate those files to be able to create some sort of representation of the form in Java…but don’t we already have the compiler for this kind of jobs?

I still wanted to be able to have both a human readable representation of the form and the right degree of flexibility to integrate the form definition into the app code.
So I thought that creating a Domain-Specific Language would have been a perfect strategy to solve the problem in an elegant and efficient way.
Writing a DSL in Java could have ended up into something like that:

I don’t think the previous code is readable nor flexible and it requires a lot of boilerplate to be written.

Kotlin to the rescue!

Unlike Java, Kotlin (take a look at my previous post about it) has a lot of features that makes it really powerful when it comes to write internal DSLs. The results are very similar to Groovy (think about a Gradle file) but thanks to its type system they could be Type-Safe.

The builders you can write with Kotlin are extremely readable and easy to understand even for people that don’t know either the language or the DSL itself. Here’s how a form built using my final DSL looks like:

Type-safe builders

Some Kotlin important features

To grasp how Type-safe builders work in Kotlin we need to understand some key Kotlin features and how they can be combined together:

Higher-Order Functions and Lambdas: in Kotlin we are allowed to write functions that have functions as parameters or return type (higher-order functions) and functions that are not declared, but are passed immediately as an expression (lambdas). Because of this, we can write things like:

transformWith("some/path/to", { path: String -> path.split("/") }) // -> [some, path, to]
// Functions which have a function as the last parameter can be written as follow
transformWith("some/path/to") { path -> path.split("/") } // -> [some, path, to]
// If the lambda has only one parameter it can be ommitted and referenced as "it"
transformWith("some/path/to") { it.split("/") } // -> [some, path, to]

Extension Functions: they allow us to extend a type with functions without modifying the original class. They are useful to add functionalities to classes we don’t have control on or to create utility methods without the need to create “Utils classes” that contains static methods, as we are used to as Java developers. To continue the previous example we can write:

Basically function literals with receiver are extension functions that can be passed to other functions.

Wrapping up

Now we have all the elements required to understand and write a Type-safe builder.

Combining the above mentioned Kotlin features we can now write a function and name it form. This function will take as parameter a function literal with receiver usually called init() and will do the follow:

create a new Form object

call init() on it (that is using it as the receiver of the function literal)

Taking advantage of Kotlin syntactic sugar we can use form() passing it the init() function as a lambda and call methods on the Form object to build it as follow:

val builtForm = form() {
// Here we can take advantage of the compiler and, as a result, of the IDE code completion
field("key1") // == this.field("key1") where this is the object create by form()
field("key2")
}
builtForm.getFields() // -> [Field("key1"), Field("key2")]

As you can see Type-Safe builders are an extremely powerful and useful feature of Kotlin and they allow you to write very complex DSLs with a really readable and clear syntax. They give you a lot of flexibility letting you combine multiple builders to create a domain language that can meet your requirements.

If you want to learn more about this subject check out the official documentation or, for example, kotlinx, an official project from the Kotlin team that allows you to create HTML documents with a custom DSL entirely written with Type-safe builders.

About

Facile.it relies on a big and keen crew of developers. Since 2008, the group’s projects are based on PHP, and as time goes by new technologies become part of corporate know how. This blog allows our developers to share tips and direct experiences with new technologies.