The concept of DSL or domain-specific language is pretty simple, it gives a context of what we doing, for example, a common Android Gradle script will have the following structure:

android {

compileSdkVersion 26

defaultConfig {

applicationId "com.example.you"

minSdkVersion 15

targetSdkVersion 26

versionCode 1

versionName "1.0

}

}

Inside the Android section, we put android specific configuration and inside defaultConfig we put the general configuration, by this structure we know the context of each section.

The same idea of structure the use of API in that way can be applied also on single class with specific responsibility, for example, if we want to write an article builder class it will be nice if we could use this class in the following way:

val articleBuilder = ArticleBuilder()

articleBuilder {

title = "This is the title"

addParagraph {

body = "This is the first paragraph body"

}

addParagraph {

body = "This is the first paragraph body"

imageUrl = "https://path/to/url"

}

}

This is a very declarative way to use this article build, it’s almost describing the way we build the article in natural language.

In kotlin it’s very easy to build such small DSL since the language provides us a lot of tools. The first step will be adding invoke operator to the class which gets as parameter lambda with receiver:

class ArticleBuilder {

operator fun invoke(block: ArticleBuilder.() -> Unit) {

block()

}

}

In this simple snippet we use 3 different tools from the language, in Kotlin we can override several operators, one of them is the invoke operator, this operator allowed us to call to a piece of code of the class without visually call to any function name so the expression articleBuilder({…}) is valid. The second tool is the option to omit the parentheses if the function has only one parameter which is a lambda, so we call replace the previous call to the invoke operator with articleBuilder{…}. The third tool is lambda with receiver, this allowed us to pass extension lambda to class so inside the curly braces we have implicit “ArticleBuilder.this” so we call refer to any public API of the class, so we can inside the curly braces set the title like this articleBuilder{title = “title”}.

The second step will be to add title property to ArticleBuilder class:

class ArticleBuilder {

lateinit var title: String

}

Since the property is public we can use it inside the outermost braces, articleBuilder{title = “title”}

The third step will be to enable to add some paragraph, in order to use it we will first add Paragraph class with 2 properties, body and imageUrl:

class Paragraph {

lateinit var body: String

var imageUrl: String? = null

}

Now we need to add a paragraph and this will be done by adding a new method with the name addParagraph which gets as a parameter a lambda with Paragraph receiver, this will allowed us to set the paragraph fields:

fun addParagraph(block: Paragraph.() -> Unit) {

val paragraph = Paragraph()

paragraph.block()

paragraphs.add(paragraph)

}

So our final code and use of the code is:

class Paragraph {

lateinit var body: String

var imageUrl: String? = null

}



class ArticleBuilder {

lateinit var title: String

val paragraphs = mutableListOf<Paragraph>()



operator fun invoke(block: ArticleBuilder.() -> Unit) {

block()

}



fun addParagraph(block: Paragraph.() -> Unit) {

val paragraph = Paragraph()

paragraph.block()

paragraphs.add(paragraph)

}

}



fun main(args: Array<String>) {

val articleBuilder = ArticleBuilder()

articleBuilder {

title = "This is the title"

addParagraph {

body = "This is the first paragraph body"

}

addParagraph {

body = "This is the first paragraph body"

imageUrl = "https://path/to/url"

}

}

}

This nice technique to use on new APIs that I want to add but what about exist API, for example what if I want to take the TransitionSet API and replace the builder pattern with DSL, let try to apply a simple Transition set to remove one view and bring in another view, in the Java world this should be something like this:

Transition transition = new TransitionSet().

addTransition(Slide(Gravity.TOP).addTarget(text1)).

addTransition(Slide(Gravity.BOTTOM).addTarget(text2)).

setDuration(1000).

setInterpolator(new FastOutSlowInInterpolator()).

addListener(new Transition.TransitionListener() {

@Override public void onTransitionStart(Transition transition) {



}



@Override public void onTransitionEnd(Transition transition) {

text1.setVisibility(View.GONE);

}



@Override public void onTransitionCancel(Transition transition) {



}



@Override public void onTransitionPause(Transition transition) {



}



@Override public void onTransitionResume(Transition transition) {



}

});

In Kotlin we can convert is to DSL format and this will be like:

val transition = TransitionSet()

transition {

addTransition {

Slide(Gravity.TOP).addTarget(text1)

}



addTransition {

Slide(Gravity.TOP).addTarget(text2)

}



duration = 1000

interpolator = FastOutSlowInInterpolator() addEndListener {

text1.visibility = View.GONE

}

}

At least for me the DSL choice give me a better understanding. Since we want to call to exist API from our code we can take advantage of extension function and some additional functionality to TransitionSet, we first should add invoke operator that get lambda with receiver so we can apply the outer most braces and some additional functions that get lambda with receiver:

operator fun TransitionSet.invoke(block: TransitionSet.() -> Unit) {

block()

}



fun TransitionSet.addTransition(block : TransitionSet.() -> Transition) {

addTransition(block())

}



fun TransitionSet.addEndListener(block: TransitionSet.() -> Unit) {

addListener(object : Transition.TransitionListener {

override fun onTransitionEnd(transition: Transition?) {

block()

}



override fun onTransitionResume(transition: Transition?) {



}



override fun onTransitionPause(transition: Transition?) {



}



override fun onTransitionCancel(transition: Transition?) {



}



override fun onTransitionStart(transition: Transition?) {



}



})

}

We saw how easily we can create a DSL in Kotlin for new APIs that we build or for more convenient (if you are DSL fan like me) way to consume exist API. Happy DSL creation!