When writing Kotlin DSLs, @DslMarker helps avoid confusion by controlling which functions and properties are visible in nested scopes. It keeps your code clear and prevents mistakes.
DSL scope control with @DslMarker in Kotlin
@DslMarker annotation class MyDslMarker @MyDslMarker class Outer { fun outerFunction() {} fun inner(block: Inner.() -> Unit) { Inner().block() } } @MyDslMarker class Inner { fun innerFunction() {} }
The @DslMarker annotation defines a marker annotation class.
Apply your marker annotation to DSL builder classes to control scope visibility.
@DslMarker annotation class HtmlTagMarker @HtmlTagMarker class Html { fun body(block: Body.() -> Unit) { Body().block() } } @HtmlTagMarker class Body { fun p(text: String) {} }
@DslMarker annotation class ConfigMarker @ConfigMarker class Config { fun database(block: Database.() -> Unit) { Database().block() } } @ConfigMarker class Database { fun url(value: String) {} }
This program defines a simple robot DSL with @DslMarker to separate Robot and Move scopes. Inside move block, you cannot call Robot's stop() directly, preventing mistakes.
@DslMarker annotation class MyDsl @MyDsl class Robot { fun move(block: Move.() -> Unit) { Move().block() } fun stop() { println("Robot stopped") } } @MyDsl class Move { fun forward() { println("Moving forward") } fun backward() { println("Moving backward") } } fun robot(block: Robot.() -> Unit) { Robot().block() } fun main() { robot { move { forward() // stop() // This would be an error due to @DslMarker } stop() } }
@DslMarker helps the compiler warn you if you try to call functions from the wrong nested scope.
Without @DslMarker, nested DSL blocks can access all outer scopes, which can cause confusion.
You can create your own marker annotation with @DslMarker to suit your DSL design.
@DslMarker controls which functions are visible inside nested DSL blocks.
It prevents accidental calls to outer scope functions inside inner blocks.
Use it by creating a marker annotation and applying it to DSL builder classes.