Reputation: 19926
I'm trying to create an easy-to-use html generator for a personal project. I thought I would use extension functions to be able to generate an html programmatically using something like this:
html {
head {
title("Site title")
}
body {
div {
// stuff in div
}
}
}
For that I declared an interface:
fun interface TagBlock {
operator fun Tag.invoke()
}
Where Tag
would be the class designating the specific tags, like html
, body
, div
etc:
class Tag(val name: String)
I now tried to create a function which accepts the earlier mentioned interface and returns a tag:
fun html(block: TagBlock): Tag {
val html = Tag("html")
// invoke `block` with `html`
return html
}
I'm stuck on how to invoke the provided parameter block
. The following all don't work:
block(html) // Unresolved reference
block.invoke(html) // unresolved reference
html.block() // Unresolved reference: block
Where am I doing something wrong?
Upvotes: 2
Views: 275
Reputation: 37710
The invoke()
operator you're declaring has 2 receivers:
TagBlock
Tag
You need to provide the dispatch receiver in the context of your call for it to work. You can do this with the library function with()
:
fun html(block: TagBlock): Tag {
val html = Tag("html")
with(block) {
html.invoke()
}
return html
}
This may or may not be the usage experience you were looking for, though.
A more idiomatic approach in Kotlin would be to just take a function type as input:
fun html(block: Tag.() -> Unit): Tag {
val html = Tag("html")
html.block()
return html
}
Upvotes: 3