I’m brand new to Kotlin, and have never used OOP ever. I’m trying to learn this language from scratch. I’m reading books, taking online courses, etc.
There are several concepts that I just can’t comprehend, and one of them at this time is “receivers”. In the Big Nerd Ranch book, chapter 9 “Standard Functions”, the “let”, “run”, “apply”, etc. scoping functions are introduced.
I’m having the darndest time understanding these functions and how they are all different. I think one thing that will help me understand these functions better, is understanding what a “receiver” is in general. I’ve found just a few explanations online, particularly over at stackoverflow, but I’m hoping somebody can speak more to my level, explain things a bit more simply and concisely.
So, what exactly is a receiver?
The question is answered pretty thoroughly in the official documentation. Have you read it? The same goes for scope functions. There are also several very low level articles about scope functions (just enter kotlin scope functions
in google). StackOverflow is not the best place for learning kotlin. The starting point is the official documentation, then google for articles.
ADDENDUM: Those two particular questions are not about OOP, they are closer to kotlin functional side.
Indeed, I have read those resources including the official documentation. Indeed, I have done much google searching and reading. I find the official documentation to be too official–what I mean is that it is clearly written for software design professionals and experts, and I find it difficult to understand at my current level; especially when it gives the definition of a function using actual computer code and generics (generics are yet another thing I’m having difficulty grasping). For example, the Let function:
inline fun <T, R> T.let(block: (T) -> R): R
What I have found when trying to read and understand receivers and the scoping functions, is that many resources talk about a difference between “this” and “it”. These resources will say that the difference between let and run is that let references “it”, and run references “this”, as if I am supposed to understand based off of this fact alone. So, this in turn has been another point of confusion for me. I feel like that if I can better understand what a receiver is, perhaps I can better understand the difference between “it” and “this”, and then better understand the scoping functions.
I really learn best by asking questions. I can’t ask books, online tutorials, or even the official documentation questions. My hope was that I could join a group of knowledgeable Kotlin-ers and ask questions in a safe and understanding place, and perhaps get some teaching and explanations at my level.
I’d like to better understand what a receiver is, and how “this” and “it” relate to a receiver, what the difference between “this” and “it” are, and how they are different.
Thank you!
OK, I just needed to check that. To many people are asking questions without trying to read first.
Let’s assume that we have the following code:
class A{
val b: Int = 2
fun doSomethingWithB(){
println(this.b) // this will work because `this` references the object instance of A
println(b) // this will work as well, because inside the object, you can omit a reference.
}
The this
identified points to the enclosing instance of the class. And it could be committed since we are inside of this instance. The receiver works the same way. The function called with a receiver is not actually inside the class, but public methods and field of the object could be called as though as from the inside.
For example, you can do this:
fun A.doOtherWithB(){
println(b) // called as though as from the inside
}
Thank you for the response. I’m not sure if I am understanding exactly what you are saying though.
That looks like a good example of the usage of “this”.
But I’m still not fully understanding what a receiver is.
I have continued to read sources, blogs, forums, code examples etc to try to understand better, and I do feel like I’m getting a better grasp.
From what I understand, a receiver is simply some object that has an extended function called on it, right? Nothing more complicated than that?
Now when it comes to “this” vs “it”: these are just shortcut keywords. “This” is used to refer to the object or “receiver” that an extended function is called on, while “it” is used to refer to a parameter being passed in a lambda/anonymous function. Do I have that right?
They clearly refer to different things, but both are still shortcuts available to make code more concise. I’m assuming that “this” and “it” can NOT be used in the same block of code, is that correct? For example, with Kotlin’s standard functions, like Let, “this” isn’t available because the scope is on the receiver as a parameter, not an object, whereas “it” can be used to refer to the parameter which is the receiver itself. But for functions like apply and run, “this” can be used because the receiver isn’t being passed into the lambda as a parameter, but they are acting on the receiver as an object, whereas “it” can’t be used because there is no parameter scoped in the apply or run function call.
Am I getting it?
Yes.
Yes and no. For for functions like let
and run
this is right. You can construct a function which takes a lambda with both it
and this
.
fun foo(code: String.(Int) -> Unit){
"test".code(5)
}
fun main(){
foo {
println(this)
println(it)
}
}
Here the code
lambda has a reciever (of type String
) and a single parameter (of type Int
). So you can mix this
and it
but I can’t remember any real world example where this is done.
So the reciever is the part in an extension function before the function name. It pretends to be the class the function belongs to. It can either be accessed with this
but same as for normal methods you can omitt the this
keyword for properties like this.foo
or function calls like this.bar()
.
It is another special case. Normaly when you create a lambda you need to name the parameters of that lambda
someList.filter { entry -> entry != foo }
However if a lambda only has a single parameter it will have a default name it
.
someList.filter { it != foo }
If you want to you can still give it any name you like with the above syntax.
Thanks Wasabi!
This lambda having a receiver is a new concept for me, and I’m confused about how this code is working. Would you be able to point me to several resources where I can find out more about passing a receiver in a lambda?
Thanks!!
Not sure I can point you to a tutorial about it or anything specificly about this. There is a short paragraph about it in the official documentation:
https://kotlinlang.org/docs/reference/lambdas.html#function-types
Function types can optionally have an additional receiver type, which is specified before a dot in the notation: the type
A.(B) -> C
represents functions that can be called on a receiver object ofA
with a parameter ofB
and return a value ofC
. Function literals with receiver are often used along with these types.
As well as a part at the end of that page:
https://kotlinlang.org/docs/reference/lambdas.html#function-literals-with-receiver
This SO answer could also be helpful
A receiver is a special function argument. It differs from ordinary arguments in the following ways:
- it does not have an explicit argument name
- its implicit name within the function is “this”
- its type is not provided within the paranthese after the function name, but instead before the function name separated by dot
- when calling the function, the receiver’s value is not provided within the paranthese after the function name, but instead before the function name separated by dot
- when calling the function, the receiver’s value does not need to be provided explicitly. If the current scope has a “this” already it can be provided implicitly
- within the function body “this” can be omitted when accessing the receiver’s members or other extension functions
I am sure there are some more differences, but at your current level this should get you started understanding them.
I am not sure if it will help you, but you can look through my article on more philosophical aspects of receivers. The idea is that in kotlin, the function not only have its arguments, but it also has a scope or context, where it is called. You have the same thing in any language that have objects with member functions, but in Kotlin you can detach the function from object and keep the reference to the context in the receiver. The receiver is designed that way in order for internal function logic to be the same when called inside and outside of the object.
Here is a method call expression:
agent?.takeAction()
The ?.
operator is used to create that method call expression. The expression on the left of the ?.
is called the “receiver” of the method call.
Now, let’s look at a call to let
.
agentFactory.createAgent()?.let { agent ->
agent.takeAction()
}
The “receiver” of the let
method call is the value of agentFactory.createAgent()
. The agent
variable in the lambda is bound to that value.
I could have written that code as
agentFactory.createAgent()?.let {
it.takeAction()
}
If you do not specify a parameter in a single-parameter lambda, the parameter is implicitly given the name it
. Just like agent
before, it
is bound to the value of the receiver of the let
method call: agentFactory.createAgent()
.
I hope that helps.
Yes. It does indeed help. Just to quickly clarify the “?.” operator. You stated is it used to create a method call expression. From what I’ve been reading, that specific operator is used as a null safety operator/check, only running the code following the ?. operator if the receiver(?) isn’t null. Is that still true?
Yes indeed. Think of thing?.method(param) { //Do Stuff }
as being basically syntatic sugar for the following code:
val localThing = thing
if (localThing != null) {
localThing.method(param) { //Do Stuff }
}
(Notice the rescoping there. This basically captures the current value of thing
, checks if it is not null, and then runs the method on it. This is what provides the power of extension scoping functions and makes stuff like thing?.let { }
a really idiomatic way to do a null check and capture the value in just a couple characters in a clear way.)
I’ll throw my own, hopefully simple, explanation in.
When you call foo.bar()
, the object foo
is the receiver of the function call. This means that in the context of bar()
there is a magic parameter called this
which contains a reference to the object foo
. The function bar()
can use that to access properties and call functions in foo
. For example, if foo
has a property baz
then from within the function bar()
you could access it with this.baz
. Becuase this
is magic, you can leave it out and the compiler will know what you are talking about, so from within bar()
you could directly reference baz
without the this.
part.
Kinda. The .
, ?.
, and ::
operators are all called navigation operators. They are binary (take two arguments) infix (operator sits between the arguments) operators.
It’s best not to think of “executing the code” so much as “evaluating the expression”.
The .
and ?.
operators are used for calling methods and referring to properties. An expression using ?.
will evaluate to null
if the expression on the left is null
and it will do so without evaluating the method arguments (if any).
In this case it may help to know a bit (more) about the history of OOP: in the far past, you only had functions, like print(“hello”). As I learned while working on a large C89 codebase, this could lead to nasty maintainability problems if programmers were not very disciplined. But suddenly, methods were invented, which were functions that were called on objects, so number.pow(2.0). Possibly to make clear that this was new, innovative stuff, and to distinguish it from the lame, old-fashioned way of using functions to do things, people called the function a ‘message’, and the object the function was called on was called the ‘receiver’.
So in politician.lie(), the politician is the receiver. “lie” was once called the message, nowadays people would call it a “method”
In cat.lie(), the cat is the receiver.
This may be a bit unintuitive, as actually the cat and the politician are the ones doing things, not passive as one may expect a receiver to be, but I suppose programmers liked to be the ones giving the orders which were then received (and executed) by obedient objects.
I must say that while I’ve programmed professionally in over half a dozen of languages, the run and let and apply were also things that I had great trouble remembering when I started with Kotlin (and lambda’s with receivers definitely gave me a headache and went in one ear and out of the other!). Only after I had written (and transformed from Java) hundreds of lines of Kotlin code the Kotlin basics began to feel natural enough for me that picking up run/apply etc. became easy enough for me to do occasionally. Because you really don’t need run and apply and such to make working software! So personally I would not worry about every detail, option and subtlety of Kotlin just yet. Just start programming with the things you understand now, try learn from the hints in IDEA (if you’re using that) and in time you will likely understand more and more, and let and apply and lambdas with receivers will not only become understandable, you’ll also enjoy understanding them much more since you will also develop the understanding needed to see when they are advantageous to apply and the skill to apply them successfully.
Part of this is the fun of slowly mastering a skill, and I think that, looking at development speed and code maintainability, Kotlin is a skill that is worthwhile to master…
@EWLameijer
Thank you! I appreciate this response!