5

I'm trying to crate a type-safe groovy-style builder in Kotlin like it's described here. The problem is visibility of lambda receivers in nested lambdas. Here is a simple example.

html {
    head(id = "head1")
    body() {
        head(id = "head2")
    }
}

Receiver of the nested lambda is Body that doesn't have the 'head' method. Nevertheless this code compiles and prints into this:

<html>
    <head id="head1"></head>
    <head id="head2"></head>
    <body></body>
</html>

It is expected but is there any way to get compilation error on the inner head?

Alexey Pomelov
  • 196
  • 1
  • 9

2 Answers2

4

As of Kotlin 1.0 this is not possible. There is an open feature request for this functionality.

yole
  • 92,896
  • 20
  • 260
  • 197
3

This is possible since Kotlin 1.1, thanks to the introduction of the @DslMarker annotation.

Here's an example of how it can be used:

@DslMarker
annotation class MyHtmlDsl

@MyHtmlDsl
class HeadBuilder

@MyHtmlDsl
class BodyBuilder

@MyHtmlDsl
class HtmlBuilder {
    fun head(setup: HeadBuilder.() -> Unit) {}
    fun body(setup: BodyBuilder.() -> Unit) {}
}

fun html(setup: HtmlBuilder.() -> Unit) {}

fun main(args: Array<String>) {
    html {
        head {}
        body {
            head { } // fun head(...) can't be called in this context by implicit receiver
        }
    }
}

See also the documentation at the announcement linked above.

zsmb13
  • 85,752
  • 11
  • 221
  • 226