In Kotlin, you have standard extension functions like
T.run(...)
, T.apply(...)
, T.let(...)
, T.also(...)
.
Each of these do a very comparable job - inlining some code, such that you don’t have to declare a variable or use a variable. The difference is that 2 of these have a function with a receiver, instead of a parameter, and 2 of these return the result of the function, as opposed to the parameter input (so the function returns Unit), such that there’s one for each combination of these.
I’ve always found it difficult to memorize which of these does exactly what. It’s too late to change now, but I think it would be possible to make it much more intuitive by allowing the caller to decide whether the first parameter is a receiver. Obviously, this idea carries over to all lambda functions. That way, you only need 2 of these. It doesn’t functionally change anything, other than that no 2 separate functions are required to do the exact same thing - with a slightly different syntax.
That could reduce it to T.run(...)
and T.apply(...)
for example.
As for how this would look syntactically, the simplest way would be to explicitly declare the parameter name “this”.
It’s a little long this way though, looking for better options.
1 Like
I think there was a discussion about this before, but I could not find it. I think the best idea for a syntax was also
T.run{ this -> ... }
I’m personally not sure what I think about it. Yes, it would make the usage of run, apply, let and also easier as you no longer have to remember which takes a receiver, but the more I think about it I feel like this is the only advantage. I mean sure, it would let you use this
in a forEach
loop or in map
but I don’t really think this is that much of an advantage.
Most of the time they are used for scoping reasons, to switch the context or to in case of run
and apply
sometimes to just have less to type (when changing multiple fields of a class). But when you think of the scoping usage 90% of the time I want to or have to make this decision when I write define the function not when I call it.
Yes most of the time it wont hurt to use a receiver instead of a parameter but I personally think this is meant to be used if you want to switch the context of the scope. Imagine a function that lets you open a file, than do some stuff with it and automatically closes it at the end. In that case I would argue that switching the context is not necessary. I normally think of it like this. The thing doing the action should be this
, so if I am using a resource I don’t want to switch the context, but if I am switching into a different domain with different actions associated I want to use a receiver and not a parameter. Obviously this is just my way of doing this and I can see valid reasons why you sometimes would want to decide at the caller to use a receiver or a parameter, but most of the time I don’t think it is necessary.
That being said, I would support this idea if there was a good syntax supporting it. I’m just not sure about
let { this -> ... }
- it’s more verbose than just
run
- Sometimes there are situations where you have to use a receiver (decided by the function), because of scoping reasons. In that case you would either have to have "
this ->
" both at call site and as part of the function signature or it would be unclear why in that special case the receiver is switched (hope this makes sense and I explain this properly).
So in conclusion I guess there are good reasons for and some good reasons against it, so yeah. I’m not really sure what the best solution is.
Maybe someone of the Kotlin team could give some insight why they decided to handle this in the way they did. @ilya.gorbunov