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
| Concept | Description |
|---|---|
| Lambda with receiver | Allows calling functions on an implicit receiver object inside a lambda. |
| DSL context class | Class that holds functions and properties used inside the DSL. |
| Extension function | Function that adds behavior to existing classes, used to build DSL blocks. |
| Builder function | Function 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.