Kotlin DSL — know your limits

Kotlin DSL — know your limits

Kotlin DSL —defining mandatory parameters

Or why I wrote another Android library

Since Kotlin was introduced, multiple libraries have opt-in for DSL implementation of their API.
Kotlin DSL is a great tool, it makes your API more readable and easier to use, and if you haven’t gotten around to play with it, I strongly encourage you to give it a try.

But this article is not meant to explain how to implement an API based on Kotlin DSL, there are some great articles about it out there.
Nor am I going talk about how awesome Kotlin DSL is (and it is).
Instead, I’ll focus on the problems I faced when I tried to apply it to my own use cases.

First

https://medium.com/media/850a7346dec798d01d2c96efa93a19a1/href

A nice example for DSL can be found in an Android library called Anko, which allows laying out UI components programmatically (might look familiar to Flutter/Jetpack devs):

verticalLayout {
editText {
hint = "Name"
textSize = 24f
}
button("Say Hello") {
onClick { toast(“Hello!”) }
}
}

Looks pretty easy to understand, right?

So, where’s the catch?

Let’s take a closer look:

button("Say Hello") {
onClick { toast("Hello!") }
}

As you can see, the button text is supplied as a parameter since it’s mandatory for the creation of a button, as opposed to the onClickListener which is optional and therefore defined inside the lambda block.

For a single parameter, this still seems quite readable, but what if there were multiple mandatory parameters?

After giving DSL a try, what I soon found out is that I had the same problem as with plain old Builders — receiving mandatory parameters.

What’s the solution then?

The obvious solution is to verify all mandatory parameters are set as part of the final object build, but that’s a runtime solution and a sign of bad API in my books.

Another solution I would use in builders was to define mandatory parameters in the constructor.
The same trick works for DSL, but I found it contradicts one of the purposes of DSL — being readable.

The final option I tried is using Kotlin Contracts, which I quickly ruled out for two reasons:
1. I didn’t feel it was mature enough.
2. They only apply for top-level functions, not exactly what I was looking for.

https://medium.com/media/b524e2fd23b6d1b3287bb1bc5bdd2f55/href

My goal was clear — a compile-time verification of mandatory parameters.
I wanted a simple solution to this common problem.

Introducing DSLint

Eventually, I decided to be my own best friend and implement a solution to this problem using a custom Android linter library (sorry non-Android devs).

So here’s how it works, let’s say I was to implement a Person builder using DSL and wanted to make sure the person’s name was set.
All I need to do is annotate the class and the name property and let my custom linter do the magic:

@DSLint
class Person {

@set:DSLMandatory
var name: String
}

Clean and simple.
I made it open source, so you can either use it in your project or clone the repo and create your custom solution based on it.

You can grab a look here:

ironSource/dslint

To sum up

As I previously said, DSL is a great tool and I encourage you to try it but also be aware of its limits.
I hope you enjoyed reading and I look forward to hearing about the challenges you faced when trying out DSL and how you approached them.

Click the 👏 to say “thanks!” and help others find this article.

To be up-to-date with great news on Kt. Academy, subscribe to the newsletter, observe Twitter and follow us on medium.

If you need a Kotlin workshop, check how we can help you: kt.academy.


Kotlin DSL — know your limits was originally published in Kt. Academy on Medium, where people are continuing the conversation by highlighting and responding to this story.