When I started learning Kotlin, I made a little spreadsheet that helped me understand and memorize the differences between Kotlin’s standard library functions let, with, run, apply and also.
I think it might be of help to other people also, so here it is:
I would tell people concentrate on the main 4 and don’t bother with with. I find with redundant and never use it.It is then easier to remember the other 4.
I don’t think they are redundant at all. I was playing with Kotlin back in the days before apply and also were added (just happened to be looking at my old posts and my second post here was requesting what became the apply method).
When you start nesting them it is very handy to be able to choose beween this or it. I actually proposed variations on apply and run that added an additional parameter but I think the addition of also (referred to as tap in that thread) was considered sufficient.
The power of with is in fact really understated. It de-facto allows context oriented programming. For example you can add some additional features to existing objects when running them inside specific context. Like this:
object ContextA{
val <T> Map<String, T>.something
get() = this["A"]
}
object ContextB{
val <T> Map<String, T>.something
get() = this["B"]
}
val map Map<String, String> = ...
with(ContextA){
doSomething(map.something)
}
You can even pass the context as a parameter. I find it really fascinating.
In fact, our two examples are not the same – and to be fair, mine is wrong. Because with returns the value from the lambda, my thing will actually end up with the value Unit. In order to fix it, I would have to do:
val thing = with (ANewThing())
{
aProperty = value1
anotherProp = value2
this
}
Which is kind of hideous. BUT! Had I been in my right mind when I wrote this, I might have used a more palatable example:
val immutableThing = with (ImmutableThing.builder())
{
setAProperty(value1)
setAnotherProp(value2)
this.build()
}
Which illustrates what I meant with the whole Java interop.
All of which is largely moot, since @darksnake has already gone six levels deeper than the rest of us…
I’m late to the party, but shouldn’t let be called map?
I believe it would feel more natural, as everyone understands that the input is the receiver, and accessed inside the lambda as a parameter, and that the return value is the lambda’s value.
Defined on nullable types, it would even be an equivalent to a Optional/Maybe’s flatMap.
Yes, let is analogous to Optional’s flatMap method. I was also thinking that it could have gotten a better name. But you need to be careful not to limit usecase with a name that is too specific.
This is one of the use cases that would look strange with map as name:
car.passenger.let { person ->
log(person.name)
anotherSideffectWith(person)
}
But that would be a strange use case for let, because it’s not using the return value.
It makes more sense to use also in this case. Or does anotherSideffectWith() return something?
Maybe the reason why it’s not called map is that map is already commonly used on collections, and it would be very strange to have it behave differently there compared to every other object.
Anyway, I find the names confusing but can’t seem to find anything short that actually reads natural
Exactly my point, yes, but I was rather wondering why it was named let and not map.
The need for a different name seems obvious for objects of type Iterable, Collection, etc. so I get that it can’t be named map without being confusing. But why let?