I'm trying to understand the following code (source).
class HTML {
fun 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
}
What I really cannot grasp is the line
html.init() // pass the receiver object to the lambda
What is happening here?
Can somebody please explain in simple words what is going on here?
First, let's make this example a little easier and see what the problem is then.
We could build the html function like this:
fun html(init: (HTML) -> Unit): HTML {
val html = HTML()
init(html)
return html
}
This would be easier to grasp (at first), because we are just passing a usual one-parameter lambda to the html function.
But now the call-site is not builder like:
html { it: HTML -> // just for clarity
it.body() // not very nice
}
Wouldn't it be nice if we could invoke body() inside html without it? That's possible! All we need is a lambda with receiver.
fun html(init: HTML.() -> Unit): HTML { // <-- only this changed
val html = HTML()
init(html)
return html
}
See how html is passed as an argument to init like before?
Of course, we can invoke it like this too: html.init() as shown in the example. The instance of HTML becomes this inside the block of the lambda.
Now, we can do this:
html {
this.body()
}
Since this can be omitted, we arrive here:
html {
body()
}
So, in the end lambdas with receivers make the code more concise and allow us to use a nice builder syntax.
Here is step by step explaination:
1. Creation of function, receiver type lambda.
fun html(init: HTML.() -> Unit): HTML {
here function html accept a parameter init of type HTML.() -> Unit i.e. it indicated that it is a receiver of HTML and can only be called with help of a real HTML object. And : HTML indicates that the function obviously returns HTML object.
2. call of init at html
html.init()
Here init() function is called as a receiver of HTML by a real HTML object.
Alright enough of formal talking, Here is what a receiver is:
So if you remember extension function defined as fun A.myFun(...): ReturnType {}, in that case you get a variable this which act as an instance of type A it was called on.
Similarly receiver lambda gives you a this variable inside that,
In a particular example:
class A {
fun thisCanBeCalledByAInstance() {
println("I've got called")
}
}
fun main() {
val receiver: A.() -> Unit = { // this: A
thisCanBeCalledByAInstance() // prints: I've got called
// or traditional way this.thisCanBeCalledByAInstance()
}
val a: A = A()
a.receiver()
}
Here you were able to call the method(function) from the instance of A even if it was lambda because it was a receiver.
PS: For simple langauge you can think html.init() as init(html) but html is not a parameter but instead works as this vaiable inside the lambda
This is why you were able to call body() on that lambda, because implicitly you were calling this.body() and this has came from html.init()'s html object.
Related
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?
The invoke() operator you're declaring has 2 receivers:
the dispatch receiver TagBlock
the explicit receiver 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
}
For example:
private fun TextView.onEndDrawableClicked(onClicked: (view: TextView) -> Unit) {
this.setOnTouchListener { v, event ->
var hasConsumed = false
if (v is TextView) {
if (event.x >= v.width - v.totalPaddingRight) {
if (event.action == MotionEvent.ACTION_UP) {
onClicked(this)
}
hasConsumed = true
}
}
hasConsumed
}
}
In the example above we see extension function. I know what it is and can use use/create similar ones. But there's onClicked: (view: TextView) -> Unit in the example's parameters. What is this? Callback? I see this kind of parameters too often, but don't have any idea how to understand that. Does it reference to some lambda function? Can someone send me detailed manual/example of this kind of parameters/functions/whatever?
onClicked is a function that gets passed to onEndDrawableClicked. It's type is a functional type (view: TextView) -> Unit. That function, when called, expects one parameter view of type TextView and returns Unit.
So yes, it is a callback, passed as a lambda. It can be used like:
val textView: TextView = ...
textView.onEndDrawableClicked {
// code that should be executed when onClicked gets called.
}
Functions are first class citiziens in Kotlin. Meaning you can store function in variables and pass them to other functions. Or let functions return functions.
When to use?
You can pass a function whenever you want to pass behavior (rather than state) to or from your functions. So the caller can decide what to do in those cases. That allows you to write highly flexible code / API's.
Alternatively you could create an interface with one ore many functions to be called and pass an instance of that interface to your function.
When I look at sample code for the "use" function in Kotlin, I usually see something like this:
private fun readFirstLine(): String {
BufferedReader(FileReader("test.file")).use { return it.readLine() }
}
However, in the following example, I don't understand where "input" comes from, since input -> appears to be a lambda. From my understanding, everything inside of use { } must be an expression:
val streamIn = resources.openRawResource(rawResId)
val streamOut = FileOutputStream(destFilename)
streamIn.use { input ->
streamOut.use { output ->
input.copyTo(output)
}
}
"input" clearly refers to the same object that "streamIn" refers to, but I don't understand how Kotlin knows that.
everything inside of use { } must be an expression
If you looked at the signature, you'll see that use takes a (T) -> R function, so really, any function/lambda that accepts the closable thing as a parameter can be passed to it.
With that misconception cleared up, let's see what the code in question is doing.
streamIn.use { input ->
streamOut.use { output ->
input.copyTo(output)
}
}
First we see streamIn.use {, which means we are going to do something with streamIn and then close it. And from now on streamIn will be called input. Then there is streamOut.use {, which indicates that we are also going to use streamOut to do stuff, and then close it, and we are going to call it output from now on.
I don't understand where "input" comes from
It's basically giving another name to the it as in your first code snippet. Since we have nested lambdas here, we can't use it to refer to the parameters of both lambdas.
"input" clearly refers to the same object that "streamIn" refers to, but I don't understand how Kotlin knows that.
This is because in the implementation of use, there's probably a line like this:
return block(this)
block is the lambda parameter you pass to use, and this is the object on which use is called. Since input is the parameter of the lambda, it refers to this.
Now we have declared that we are going to use two resources, what are going to do with them? input.copyTo(output)! Whatever copyTo returns is going to be returned by streamOut.use, which in turn is going to be returned by streamIn.use. streamOut and streamIn will also be closed one after another.
So overall what have we done? We have basically used 2 resources at the same time and closed them afterwards. This is how you'd compose use to use multiple resources at the same time.
in the lambda, you can define a name for your object so in the following code the input is equivalent to it which is streamIn and output is equivalent to streamOut:
streamIn.use { input ->
streamOut.use { output ->
input.copyTo(output)
}
}
The reason that they define input and output is you cannot use it when you use a lambda block inside another lambda block.
use is an extension function which takes whatever calls it as a parameter.
Assume this example:
file.bufferedReader().use{
println(it.readText()) // it is actually that object that calls `use`
}
According to the API docs of Kotlin, this is the schema of use:
inline fun <T : AutoCloseable?, R> T.use(block: (T) -> R): R
The bufferedReader in my example is a closable class.
When you write somethingClosable.use { }, you are in fact passing a lambda function to it, like:
fun <T, R> function(t: T): R {
// use T and return an R
}
somethingClosable.use(function)
And inside use your function will be called.
More info on extension functions in Kotlin.
I'm trying to keep this minimal, but let me know if I'm being too minimal.
Suppose you have a class hierarchy like this, designed for generating HTML (inspired by the Kotlin tutorial; semi-pseudocode follows):
class Tag {
protected val children = arrayListOf<Tag>()
operator fun String.unaryPlus() = children.add(Text(this))
}
class TagWithChildren : Tag() {
fun head(init: Head.() -> Unit) = initializeTag(Head(), init)
fun script(init: Script.() -> Unit) = initializeTag(Script(), init)
fun <T : Tag> initializeTag(tag: T, init: T.() -> Unit): T {
tag.init()
children.add(tag)
return tag
}
}
class Head : TagWithChildren()
class Script : Tag()
class Text(val str: Text) : Tag()
Notice that Head has head and script methods while Script doesn't.
Now you can construct a template that looks like this:
head {
script {
+"alert('hi');"
}
}
Which works great! However, if the block passed to script tries to call methods that aren't available on Script, it can call the method on Head instead. For example,
head {
script {
script {
+"alert('hi');"
}
}
}
not only isn't a compile error, it's actually equivalent to
head {
script {
}
script {
+"alert('hi');"
}
}
which is super confusing, from a template author's perspective.
Is there any way to prevent method lookups from traveling up the scope like that? I only want it to look at the innermost scope.
UPDATE 11/24/2016:
Kotlin 1.1-M03 has introduced scope control, which I believe solves exactly this problem. https://blog.jetbrains.com/kotlin/2016/11/kotlin-1-1-m03-is-here/
The current behavior is intentional. Code in a lambda has access to receivers of all enclosing scopes. It is possible that a future version of Kotlin will add a modifier that will restrict a lambda with receiver to calling methods on that receiver only and not the enclosing scopes, but in the current version there's no way to change that behavior.
As a workaround, I can have it fail at runtime if I change the classes to look like this:
open class Tag {
operator fun String.unaryPlus()
// pulled up from TagWithChildren, call protected method
fun head(init: Head.() -> Unit) = addChild(Head())
fun script(init: Script.() -> Unit) = addChild(Head())
// throws in Tag
open protected fun addChild(t: Tag) = throw IllegalArgumentException()
}
class TagWithChildren : Tag() {
// overridden to not throw in subclass
protected override fun addChild(t: Tag) = children.add(t)
}
This way, every Tag has the builder methods (solving the scoping problem), but actually calling them may result in a runtime failure.
In Kotlin I have this function to wrap a transaction:
fun wrapInTransaction(code: () -> Unit) {
realmInstance.beginTransaction();
code.invoke()
realmInstance.commitTransaction();
}
How can I get access to realmInstance in the invoked code?
The easy solution here is to make code a function with receiver:
fun wrapInTransaction(code: Realm.() -> Unit) {
realmInstance.beginTransaction();
realmInstance.code()
realmInstance.commitTransaction();
}
Inside a lambda which you pass as code you will be able to use this to reference the RealmInstance and to use its members directly as if inside a member function.
Calling realmInstance.code() is just calling code with passing realmInstance as a receiver to it.
The other answers correctly demonstrate how to pass the RealmInstance object to the lambda. In addition, you can make the whole function an extension function which makes the call site a bit prettier:
fun Realm.wrapInTransaction(code: Realm.() -> Unit) {
//this is implicit
beginTransaction();
code()
commitTransaction();
}
The call site will look like this:
Realm.getInstance(this).wrapInTransaction {
createObject(User.class)
}
Change the wrapInTransaction function to accept an extensions method on realmInstance like so:
fun wrapInTransaction(code:Realm.() -> Unit){
realmInstance.beginTransaction();
realmInstance.code()
realmInstance.commitTransaction();
}
Then you can use it like:
wrapInTransaction {
println("realm instance is $this, instanceId: $instanceId")
}
Where for the sake of the example the Realm looks like:
class Realm {
val instanceId = 42
fun beginTransaction() {
}
fun commitTransaction() {
}
}
The above technique is possible thanks to Kotlin's Function Literals with Receiver that make it possible to set the this instance (receiver) within lambda function body. It makes it easy to build type safe builders that reassemble ones from Groovy or Ruby.
This answer provides more samples on the technique.