Как точно работает ktlin htmlx-строитель под капотом?

Это фрагмент, который объясняет часть сборки htmlx (из документации):

protected fun <T : Element> initTag(tag: T, init: T.() -> Unit): T { tag.init() children.add(tag) return tag } 

Главное – children.add(tag) , поэтому мы можем объявить:

 html { head {} body {} } 

потому что голова и тело являются функциями-членами html. Но как насчет тега DIV ? Я могу объявить div везде, кроме того, я могу написать что-то вроде этого:

 someEnclosingTag { (1..3).forEach { div {+"MyCustomDivFromEverywhere"} }} 

Каким образом охватывающая лямбда знает о «дочерних» лямбдах (и, соответственно, добавляет «дочерний» тег к целому html), которые могут быть объявлены повсюду?

Пожалуйста, поправьте меня, если я где-то не прав.

ОБНОВИТЬ

основанный на ответе, я закончил с помощью следующего грязно-фиктивного кода, который показывает функциональные области (некоторые аспекты закрытия) и неявное упущение приемника (надеюсь, что это может помочь кому-то):

 fun main(args: Array<String>) { Child().childFun { /*childFun lambda receiver implements parent1Fun lambda receiver, so the receiver can be omitted*/ parent1Fun { /*call from Child.() -> Unit receiver*/ someIntrestingFun() } /*same as with parent1Fun*/ parent2Fun { /*call from Child.() -> Unit receiver*/ someIntrestingFun() } } } fun Child.childFun(lambda: Child.() -> Unit): Child = genericFun(Child(), lambda) fun ParentInt1.parent1Fun(lambda: ParentInt1.() -> Unit): ParentInt1 = genericFun(Child(), lambda) fun ParentInt2.parent2Fun(lambda: ParentInt2.() -> Unit): ParentInt2 = genericFun(Child(), lambda) fun <T> genericFun(instance:T, lambda:T.() -> Unit): T { instance.lambda() return instance } interface ParentInt1 interface ParentInt2 class Child : ParentInt1, ParentInt2 { fun someIntrestingFun() { println(this) } } 

Более подробную информацию о методах построения таких DSL можно найти в справочнике по языку: « Тип-безопасные сборщики» , а на этой странице приведен пример HTML-конструктора (хотя kotlinx.html более сложный).

Как охватывает лямбда о «детских» лямбдах, которые могут быть объявлены повсюду?

Так работает разрешение функции: когда у вас есть вложенные лямбды, с приемником или нет, во внутренних можно вызвать функции члена / расширения на приемниках внешних (*) , вот очень синтетический пример:

 with(arrayListOf<String>()) { with(hashMapOf<Int, String>()) { // You can call both functions of `ArrayList` and `HashMap`: add("foo") put(1, "bar") // Even in the nested lambdas with no receiver: baz.forEach { put(it, "it = $it") } } } 

(*): В расширенных DSL область может быть ограничена с помощью @DslMarker , чтобы избежать случайного вызова функции на приемнике из внешней области.

Intereting Posts