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.
Practice
@DslMarker annotation in Kotlin DSLs?Solution
Step 1: Understand DSL nesting problem
In nested DSL blocks, functions from outer scopes can be accidentally called inside inner blocks, causing confusion.Step 2: Role of @DslMarker
@DslMarker annotation marks DSL builder classes to restrict visibility of outer scope functions inside nested DSL blocks, preventing accidental calls.Final Answer:
To limit function visibility and prevent accidental calls to outer DSL scopes inside nested blocks. -> Option AQuick Check:
@DslMarker controls scope visibility = A [OK]
- Thinking @DslMarker generates code automatically
- Confusing @DslMarker with deprecation annotations
- Assuming @DslMarker enables inline functions
Solution
Step 1: Recall syntax for DSL marker annotation
DSL marker annotations must be annotated with@DslMarkerand declared as annotation classes.Step 2: Identify correct syntax
@DslMarker annotation class MyDslMarker correctly declares an annotation class with@DslMarkerannotation:@DslMarker annotation class MyDslMarker.Final Answer:
@DslMarker annotation class MyDslMarker -> Option DQuick Check:
DSL marker annotation syntax = A [OK]
- Defining DSL marker as a function instead of annotation class
- Missing the @DslMarker annotation on the annotation class
- Using incorrect placement of @DslMarker keyword
@DslMarker
annotation class FirstDsl
@DslMarker
annotation class SecondDsl
@FirstDsl
class BuilderA {
fun a() = println("A")
fun buildB(block: BuilderB.() -> Unit) {
BuilderB().apply(block)
}
}
@SecondDsl
class BuilderB {
fun b() = println("B")
fun buildA(block: BuilderA.() -> Unit) {
BuilderA().apply(block)
}
}
fun main() {
BuilderA().apply {
a()
buildB {
b()
// Which call is allowed here?
// a() or b() or both?
}
}
}
Which function call is allowed inside the buildB block?Solution
Step 1: Understand effect of multiple @DslMarker annotations
Each DSL marker annotation restricts visibility of outer scope functions from classes annotated with different DSL markers.Step 2: Analyze nested block visibility
InsidebuildBblock (BuilderB scope), onlyb()is visible. Thea()function from BuilderA is hidden due to different DSL markers.Final Answer:
Onlyb()is allowed;a()is not visible due to DSL marker scope control. -> Option CQuick Check:
Different DSL markers hide outer scope functions = C [OK]
- Assuming all functions from outer scopes are visible
- Ignoring effect of multiple DSL markers
- Confusing which DSL marker applies where
@DslMarker
annotation class MyDsl
@MyDsl
class Outer {
fun outerFun() = println("Outer")
fun inner(block: Inner.() -> Unit) {
Inner().apply(block)
}
}
@MyDsl
class Inner {
fun innerFun() = println("Inner")
}
fun main() {
Outer().apply {
outerFun()
inner {
outerFun() // Error here
innerFun()
}
}
}Solution
Step 1: Identify cause of error
Inside the inner block,outerFun()is not directly visible because@MyDslrestricts access to outer scope functions.Step 2: Fix by explicit qualification
Use explicit labelthis@Outer.outerFun()to call the outer function inside the inner block, bypassing DSL marker restriction.Final Answer:
Error: Cannot call outerFun() inside inner block due to DSL marker scope control; fix by qualifying with this@Outer.outerFun(). -> Option BQuick Check:
Use explicit this@Outer to access outer functions [OK]
- Removing @DslMarker annotation instead of qualifying call
- Ignoring the need for explicit receiver labels
- Assuming innerFun() visibility causes error
HtmlBuilder and BodyBuilder, both annotated with the same @DslMarker annotation. How does using @DslMarker help prevent mistakes when writing nested DSL code like html { body { div { ... } } }?Solution
Step 1: Understand nested DSL structure
In nested DSLs like HTML, inner blocks (BodyBuilder) are inside outer blocks (HtmlBuilder).Step 2: Role of @DslMarker in nested builders
@DslMarker annotation restricts visibility of outer builder functions inside inner builder blocks, preventing accidental calls to outer scope functions without explicit qualification.Final Answer:
It prevents calling functions from HtmlBuilder inside BodyBuilder blocks unless explicitly qualified, avoiding accidental outer scope calls. -> Option AQuick Check:
@DslMarker hides outer builder functions inside nested blocks [OK]
- Thinking @DslMarker generates code automatically
- Assuming all functions remain visible inside nested blocks
- Believing @DslMarker disables nesting
Ready to prove it?
Pass the test and this pattern moves unknown
