0
0
KotlinHow-ToBeginner · 3 min read

How to Create DSL in Kotlin: Simple Syntax and Example

To create a DSL in Kotlin, use lambda with receiver functions that allow building blocks with a clean, readable syntax. Define classes and extension functions that let users write code that looks like a custom language tailored to a specific domain.
📐

Syntax

A Kotlin DSL typically uses lambda with receiver syntax, where a lambda block is called on an object, allowing direct access to its members without extra qualifiers. You define a class representing the DSL context and write extension functions or properties to build the DSL structure.

Key parts:

  • class: Holds DSL state and functions.
  • fun dslFunction(block: Receiver.() -> Unit): Accepts a lambda with receiver.
  • block(): Executes the lambda with the receiver object.
kotlin
class Robot {
    fun move(direction: String) {
        println("Moving $direction")
    }
}

fun robot(block: Robot.() -> Unit) {
    val robot = Robot()
    robot.block()
}
💻

Example

This example shows a simple DSL to control a robot. The robot function takes a lambda with receiver of type Robot. Inside the lambda, you can call move directly, making the code read like a small language.

kotlin
class Robot {
    fun move(direction: String) {
        println("Moving $direction")
    }

    fun stop() {
        println("Stopping")
    }
}

fun robot(block: Robot.() -> Unit) {
    val robot = Robot()
    robot.block()
}

fun main() {
    robot {
        move("forward")
        move("left")
        stop()
    }
}
Output
Moving forward Moving left Stopping
⚠️

Common Pitfalls

Common mistakes when creating Kotlin DSLs include:

  • Not using lambda with receiver, which makes the DSL less readable.
  • Using regular lambdas without receiver, forcing verbose calls like obj.move().
  • Overcomplicating the DSL with too many nested classes or unclear function names.

Keep the DSL simple and intuitive.

kotlin
/* Wrong: lambda without receiver, less readable */
fun robot(block: (Robot) -> Unit) {
    val robot = Robot()
    block(robot)
}

/* Right: lambda with receiver, cleaner syntax */
fun robot(block: Robot.() -> Unit) {
    val robot = Robot()
    robot.block()
}
📊

Quick Reference

ConceptDescription
Lambda with receiverAllows calling functions on an implicit receiver object inside a lambda.
DSL context classClass that holds functions and properties used inside the DSL.
Extension functionFunction that adds behavior to existing classes, used to build DSL blocks.
Builder functionFunction that accepts a lambda with receiver to create DSL blocks.

Key Takeaways

Use lambda with receiver to create clean and readable DSL blocks.
Define a context class that holds DSL functions and properties.
Keep DSL syntax simple and intuitive for easy use.
Avoid regular lambdas without receiver to prevent verbose code.
Test your DSL with examples to ensure clarity and usability.