I have colleagues that use `.let { ... }` all the ...
# announcements
j
I have colleagues that use
.let { ... }
all the time and don't really understand the need for the others. I know there are multiple articles on when to use
let, also, with, apply, run, takeIf, ...
But which one did you find the most useful?
s
each of them have different use cases. This video is a good introduction:

https://www.youtube.com/watch?v=YxOTU9F_YX4&t=1262

Personally, I started of using
let
a lot but by now it’s (in order of use) mostly:
apply
,
let
and
also
. I’m not sure
takeIf
belongs with the others though 🤔
m
For me, it often comes down to avoiding variable declaration, so I mostly use let, also (side effects) and apply (additional operations on created object that are not part of constructor).
j
Shameless plug: one great use case for with is to extract constants in an object
with(ConfigObject) { ... }
https://pl.kotl.in/4I3H2kfPt https://dev.to/jmfayard/with-configobject-language-kotlin-issparkingjoy-ic4
@Stephan Schroeder to be honest I have never understood the use case for takeIf
s
I can relate,
takeIf
seems a bit indulgent (/code golfing) to me https://medium.com/@elye.project/using-kotlin-takeif-or-takeunless-c9eeb7099c22
👍 1
j
Oh really nice. My TLDR is that it make sense if you use the object in the conditionnal and you do a quick return after it.
?: error("Error")
or
?: return false
v
I always use
?.let { ... }
to perform something when the receiver is not null. Example,
Copy code
val response = getResponse(url)
response?.data?.userId?.let { 
    PreferenceHelper.userId = it
}
s
Maybe you'll like my article 😀 “Let’s also apply a run with Kotlin on our minds” by Anton Spaans https://link.medium.com/wRHcV8OQm1
🎉 1
s
@Vishnu Haridas that looks more like a sideeffect to me (you don’t use the return-value of let), so
also
would be more idiomatic:
Copy code
val response = getResponse(url)
response?.data?.userId?.also { 
    PreferenceHelper.userId = it
}
On the other hand assuming that
PreferenceHelper.userId
is
null
before you set it and therefore nullable, why not use a simple assignment?!
Copy code
PreferenceHelper.userId = response?.data?.userId
👍 1
v
@Stephan Schroeder Oops! 🤦 My actual intent was to have a block of code (obviously with more lines of code) run only when the
userId
is not null. Anyway, curious to know why you said
.also
is more idiomatic than
.let
🤔
s
because
let
returns the last expression it computes, which can mess up your code, if that’s not what you’re intending (because
if-else
is also an expression and suddenly starts complaining that you need an
else
as well). So if you’re not using the value returned by
let
, it’s better to use
also
(which returns
Unit
and is therefore safe(r)). I see
let
mostly as the “single value version of `map`“.
✔️ 1
m
@Stephan Schroeder Small correction, “also” returns the value it is called on, not Unit. Which is still safer than let, as you still produce no unintended effects by calling it to the surrounding code.
👍 1
j
I like what
T.run { ... }
does differently than
T.let { ... }
but I don't like its name. I don't find it clear at all. Anybody has a better name?
l
In order, I like
also
,
with
,
let
and
apply
.
g
Almost never use
with
, only when want to use member extension function Also think that local variable and if check is better than let for null check. A lot more explicit and more readable
Use also and apply for object configuration
run
as analogue of
map
, convert one type to another (or let, depends on use case) takeIf is quite different than other scope funxtiobs, but like to use it in some cases where I want to validate object (often use for files or strings)
l
After saying my message above, I just used
takeIf
😅
j
T.run
as analogue of
map
... yes but I don't find the name
run
clear at all that it is doing this I would use it more if it had a better name Not that I have a better suggestion mind you.
m
Maybe mapSingle then? But to be consistent with the it vs this, let would have to be named that way.
j
@Wiebe Elsinga Why not
T.mapIt { }
(copy of let) and
T.mapThis { }
(copy of run) ?
g
It and this version are really strange. Also it's not like map, it also changes receiver scope
let is closer to map than run
j
Really? My mental model was -
<T>.let
is map for a single value called
it
-
<T>.run { .. }
(not
run { ... }
) is like
let
but instead of the single value being
it
, it is
this
g
I mean signature of let is closer to map, as you said both have receiver as argument
j
that's correct yes