Learning Kotlin lambdas, please review

#1

Hello
I am new to Kotlin, learning lambdas.
In the manual there is this example:

class HTML {
    fun body() { println (" inside body() ")  }
} 

fun html(init: HTML.() -> Unit): HTML {
    val html = HTML() // create the receiver object
    html.init() // pass the receiver object to the lambda
    return html
} 

html { // lambda with receiver begins here
    body() // calling a method on the receiver object
}


I want to pass one input parameter to the fun body().

Here is my code, which works correctly: (please ignore the formatting)

class HTML {
    fun body(s : String ) {  println ("body() $s") }
}

fun html( s : String = "blank", init: HTML.(String) -> Unit) : HTML {
    val h = HTML() // create the receiver object
    h.init(s) // pass the receiver object to the lambda, same as below
    return h
}

html { body("yuri") }

Furthermore, passing html( “parameter”) explicitly, still will use the parameter value passed from the call to function body(“budilov”), not from html(“NO”) call.

html("NO") { body("budilov") }

So “budilov” is printed, not the “NO”

I dont know if this is the correct/recommended way of passing a parameter.
For a start, my solution requires input parameter to use default i.e. = “default value”, else it wont compile unless I pass the same parameter twice. This tells me that I am not using best Kotlin practice here.

Can you please comment on my solution and tell me the best-practice way of doing so?

thank you

#2
    html("yuru") {
        body("${it}yuri")
    }

or

    html("yuru") { arg ->
        body("${arg}yuri")
    }
#3
html("hello") { body("$it yuri") }
html("hello", { body("$it yuri") })
html("hello", { z -> body("$z yuri") })

All of the above print the same output, which is this: body() hello yuri
What I am trying to understand is this: how does the html(“hello)” becomes the value is $it or $z in the body of the { z -> body("$z yuri") } - given they are part of the same call on one function, namely html() which takes 2 parameters - a string and a lambda.

Thank you in advance

#4

h.init(s) can be replaced with init.invoke(h, s)

In Java, HTML.(String) -> Unit is actually a functional interface: kotlin.jvm.functions.Function2 which has only one method: invoke .

1 Like
#5

what you write:
html(“first”) { body(“second”) }
with replaced sugar (not all sugar, but enough)
val second: HTML.(String) -> Unit = { string -> body(“second”) }
html(“first”, second)

what you want:
val second: HTML.(String) -> Unit = { s -> body(s + " second") }
html(“first”, second)
or with sugar
html(“first”){ body(it + " second") }
and this is actually pointless dsl, it can be useful then your HTML and BODY has separate fabric methods and HTML contains BODY typed property, and render method which print nodes

*you shouldn’t start lambdas with dsl-examples, it’s too complex
then you use functional parameter “init” you make extension for your fabric method “html” which allows you to configure created object with calls on the consumer side like:

class HTML {
    private var _body = BODY()

    fun body(init: BODY.() -> Unit) {
       _body.init()
    }

    fun render() {
        print("<html>")
        _body.render()
        print("</html>")
    }
}

class BODY {
    var text = ""
    fun render() = print("<body>$text</body>")
}

fun html(init: HTML.()->Unit):HTML = HTML().apply{ init() }

// now we use it:
html {
    body {
       text = "body"
    }
}.render()
1 Like
#6

Your first 2 variations are just a sugar code versions for the last line.
The last one should be easy to understand:
The parameter of “html(s)” get passed to “body()” by code “h.init(s)” in “html()” definition.

1 Like