Challenge 3: Extending the HTML DSL — Solution abstract class Tag(private val name: String) { private val children = mutableListOf() private var text: String = "" fun initTag(tag: T, init: T.() -> Unit): T { tag.init() children.add(tag) return tag } protected fun setText(value: String) { text = value } fun render(indent: String = ""): String { val body = if (text.isNotEmpty()) text else children.joinToString("") { "\n$it" } return "$indent<$name>$body" } override fun toString() = render() } class Html : Tag("html") { fun head(init: Head.() -> Unit) = initTag(Head(), init) fun body(init: Body.() -> Unit) = initTag(Body(), init) } class Head : Tag("head") { fun title(text: String) = initTag(Tag("title") {}) { setText(text) } } class Body : Tag("body") { fun h1(text: String) = initTag(Tag("h1") {}) { setText(text) } fun p(text: String) = initTag(Tag("p") {}) { setText(text) } fun ul(init: Ul.() -> Unit) = initTag(Ul(), init) } class Ul : Tag("ul") { fun li(text: String) = initTag(Tag("li") {}) { setText(text) } } fun html(init: Html.() -> Unit): Html { val h = Html() h.init() return h } fun main() { val page = html { body { ul { li("First") li("Second") } } } println(page.render()) } /* Output (approximately, exact whitespace depends on render()'s simple joinToString-based nesting): Notes: - Ul follows the exact same pattern as Head and Body: a Tag subclass with its own builder methods (li here) that call initTag() the same way h1/p/title already did. - body's new ul(init: Ul.() -> Unit) method mirrors head()/body() on Html — same initTag() call, just targeting a different Tag subtype. - Because Tag is a single shared base class, adding a new tag type only requires: (1) a new Tag subclass with its own builder method(s), and (2) one new builder method on whichever parent tag should be able to contain it (Body, in this case) — the rendering logic itself (render()) doesn't need to change at all. */