`run` vs `let` for this specific case :one: ``` ...
# codingconventions
a
run
vs
let
for this specific case 1️⃣
Copy code
fun getFirstAddress(list: List<Coordinates>): Address? {
        // ... do something with list

        return list.firstOrNull { it.isValid() }?.run { Address(latitude, longitude) }
    }
2️⃣
Copy code
fun getFirstAddress(list: List<Coordinates>): Address? {
        // ... do something with list

        return list.firstOrNull { it.isValid() }?.let { Address(it.latitude, it.longitude) }
    }
3️⃣
Copy code
fun getFirstAddress(list: List<Coordinates>): Address? {
        // ... do something with list

        return list.firstOrNull { it.isValid() }?.let { coordinates ->
            Address(coordinates.latitude, coordinates.longitude)
        }
    }
4️⃣
Copy code
fun getFirstAddress(list: List<Coordinates>): Address? {
    // ... do something with list

    return list.firstOrNull { it.isValid() }?.toAddress()
}

private fun Coordinates.toAddress() = Address(latitude, longitude)
5️⃣
Copy code
fun getFirstAddress(list: List<Coordinates>): Address? {
        // ... do something with list

        val firstValidCoordinate = list.firstOrNull { it.isValid() } ?: return null
  
        return Address(firstValidCoordinate.latitude, firstValidCoordinate.longitude)
    }
2️⃣ 4
4️⃣ 10
3️⃣ 1
j
I would go for a 4th option:
Copy code
fun getFirstAddress(list: List<Coordinates>): Address? {
    // ... do something with list

    return list.firstOrNull { it.isValid() }?.toAddress()
}

private fun Coordinates.toAddress() = Address(latitude, longitude)
👍 2
p
as long as we're skinning cats, could also add a constructor for
Address
that directly accepts
Coordinates
, then
?.let(::Coordinates)
I like 4 for readability, although it's a fairly minor gain in this case I feel like I would probably actually write this as 2 first and only rewrite it to 4 if I was really bothered by it
☝️ 2
e
It is definitely
let
if you don’t have an extension function. With extension function it is much nicer, though.
💯 1
m
extensions FTW! 😁
a
I need to deal with
it
all over the place when using
let
. The code would be more concise with
run
, wouldn't it?
j
It might, but it's not the intent of
run
IMO. I consider
let
to be "the map for single values", which would be the appropriate alternative here if not using an extension. If you really have a lot of `it`s (meaning the expression in the
let
is big), it might be worth using extensions
a
if
run
is intended for some specific purposes I really wish we had another
let
function similar to
run
say
letIt
m
According to Kotlin docs:
run
 is useful when your lambda contains both the object initialization and the computation of the return value.
j
@AlexD If you really want the object as a receiver, of course you should use
run
. There is real no reason for an extra scope function in the stdlib here. The point of
let
being considered as
map
for single objects is that it really does behave like
map
as well, with
it
in the lambda.
d
Copy code
fun getFirstAddress(list: List<Coordinates>): Address? {
        // ... do something with list

        val firstValidCoordinate = list.firstOrNull { it.isValid() } ?: return null
  
        return Address(coordinates.latitude, firstValidCoordinate .longitude)
    }
No need for nesting. This could be solved by a guard
1
👍 1
You even gain a name (firstValidCoordinate) so the reader does not need to care about understanding the firstOrNull
👍 2
a
so no
run
or
let
? This brings me a question about the kotlin way. Are we more tolerant of using variables than the scope functions?
d
Personally I would not use scopingin this case, no. Kotlin gives us the option to return early very cleanly (with elvis operator). This can be used here. In the end it goes down to personal preference - Would not be a blocker for a pull request approval 🙂
2
a
makes sense
g
Agree with Daniel’s solution, it how we do most of code which allows early returns and usually we do not allow block functions just to handle nullability