0
0
Kotlinprogramming~7 mins

DSL scope control with @DslMarker in Kotlin

Choose your learning style9 modes available
Introduction

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.

You build a Kotlin DSL with nested blocks and want to avoid accidentally calling functions from the wrong scope.
You want to make your DSL easier to read by limiting what is accessible inside nested lambdas.
You create multiple DSL builders that can be nested and want to prevent mixing their members.
You want to catch scope-related errors at compile time instead of runtime.
Syntax
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.

Examples
This example shows a simple HTML DSL with @DslMarker to separate Html and Body scopes.
Kotlin
 @DslMarker
annotation class HtmlTagMarker

@HtmlTagMarker
class Html {
    fun body(block: Body.() -> Unit) {
        Body().block()
    }
}

@HtmlTagMarker
class Body {
    fun p(text: String) {}
}
Here, @DslMarker is used to avoid calling Config functions inside Database block accidentally.
Kotlin
 @DslMarker
annotation class ConfigMarker

@ConfigMarker
class Config {
    fun database(block: Database.() -> Unit) {
        Database().block()
    }
}

@ConfigMarker
class Database {
    fun url(value: String) {}
}
Sample Program

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.

Kotlin
 @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()
    }
}
OutputSuccess
Important Notes

@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.

Summary

@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.