debugging challenge: ```fun main() { val root ...
# codereview
l
debugging challenge:
Copy code
fun main() {
    val root = Thing(null, 0).apply {
        addChild(1) {
            addChild(2)
            addChild(3) {
                addChild(4)
            }
        }
        addChild(5)
    }
    root.printTree()
}

abstract class Nestable(
    val parent: Nestable?
) {
    val children = mutableListOf<Nestable>()
}

class Thing(
    parent: Thing?,
    val number: Int
) : Nestable(parent) {
    init {
        parent?.children?.add(this)
    }

    fun addChild(
        number: Int,
        block: Nestable.() -> Unit = {}
    ) = Thing(this, number).run(block)

    fun printTree(indentation: Int = 0) {
        println(" ".repeat(indentation) + number)
        children.forEach {
            (it as? Thing)?.printTree(indentation + 1)
        }
    }
}
expected output:
Copy code
0
 1
  2
  3
   4
 5
actual output:
Copy code
0
 1
 2
 3
 4
 5
find the bug >:)
p
addChild
accepts a trailing lambda with
Nestable
, instead of
Thing
, as the context receiver, so you get the outermost
Thing
from
apply
as the receiver instead of the innermost...
l
very good!
k
🤯
p
interestingly, this works as-is; I'm not really sure why the resolution works the way it does in your example
l
because now
Nestable
has an
addChild
function so it takes the nearest receiver, whereas before it had to go all the way out to the root to find a receiver of type
Thing
j
If you want to create DSLs like this, be sure to check out @DslMarker to avoid this kind of issues: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-dsl-marker/ It prevents DSL functions declared on the outer elements from being used inside nested elements (unless you explicitly use a labeled syntax with
@
)