I am trying to implement my own version of typesafe DSL builder for creating web pages. I came into a problem that an extension function of the receiver of an outer lambda can be referenced by a call from a nested inner lambda, which the programmer may not mean to do so. The compiler seems unable to safe-guard the program. The problem can be illustrated as the following simplified example:
class Div {
}
class P {
}
class Anchor {
}
// Extension functions for Div to allow Div, P, Anchor elements nested in a Div element
fun Div.div(init: Div.() -> Unit) {
// code here to create a Div element and add it to the parent Div and call init()...
}
fun Div.p(init: P.() -> Unit) {
// code here to create a P element and add it to the parent Div and call init()...
}
fun Div.a(init: Anchor.() -> Unit) {
// code here to create an Anchor element and add it to the parent Div and call init()...
}
// Extension functions for P to allow only Anchor element and text:
fun P.a(init; Anchor.() -> Unit) {
// code here to create an Anchor element and add it to the parent P and call init()...
}
fun P.text(text: String) {
// code here to add the text node to the P element
}
// Top level function to create a Div element and initialize it
fun div(init: Div.() -> Unit): Div {
val divElement = Div()
divElement.init()
return divElement
}
// Test code
fun test() {
div {
div {
a {
}
}
p {
div {
// this Div is a programming mistake because a Div cannot be included in a P,
// and P does not have an extension function div() to allow this to happen.
// However, the compiler allows this to happen because the div{ } call in p{ } is
// resolved to call the receiver in the outer div{ } lambda, which allows a div{ } call.
}
}
}
}
The problem here is that the implicit “this” receiver is resolved to the outer scope if the current scope does not apply.
I wonder if there is a way to workaround it; otherwise the DSL cannot be made syntactically safe.
Regards,
VN