hi guys, yesterday i was asked an interesting ques...
# announcements
c
hi guys, yesterday i was asked an interesting question: how to deal with multiple optional values. should we go for simple null checks via if, or can we do it via a more sophisticated approach using something like let. given the following in an external module (to make it even harder to disable smart casting):
Copy code
data class Person(
        val name: String?,
        val age: Int?
)
fun moduleExternal() = Person("", 1)
and a target function with a signature of:
Copy code
fun safeCall(x1: String, x2: Int) {}
the following WON'T compile due to not being able to smart cast external module's properties:
Copy code
fun solution_no_smartcasts() {
    val person = moduleExternal()
    if (person.name != null && person.age != null) {
        safeCall(person.name, person.age) // ERROR!
    }
}
in order to get rid of the smart cast problem first, i came up with the following idea of destructuring:
Copy code
fun solution_destructure() {
    val (name, age) = moduleExternal()
    if (name != null && age != null) {
        safeCall(name, age)
    }
}
ok, but how about that "low level" if? apple's swift would solve it this way:
Copy code
if let name = person.name as? String, let age = person.age as? Int {
    // name and age are non-optional in here
}
so i thought about an extended if which "kind of" solves it:
Copy code
fun <T1, T2> ifNotNull(x1: T1?, x2: T2?, action: (T1, T2) -> Unit) {
    if (x1 != null && x2 != null) {
        action(x1, x2)
    }
}

fun solution_kotlin_lets() {
    val person = moduleExternal()
    ifNotNull(person.name, person.age) { name, age ->
        safeCall(name, age)
    }
}
what are your thoughts on this topic?
d
at the moment we’re using the same construct, just with a return type
R?
instead of
Unit
so we can use it like a
let
for multiple types. (with
multiLet(a,b) { a, b ->  done } ?: somethingElse
g
I like this approach
Copy code
fun solution_destructure() {
    val person = moduleExternal()
    val name = person.name ?: return
    val age = person.age ?: return
    safeCall(name, age)
}
Also it allows easily replace
return
with
error()
or some other null handler
But
ifNotNull
works too, especially for cases where you want to receive result of expression, not just early
return
or
break
d
i’ve used the
?:
approach with exceptions to pass the error futher down and handle it correctly / display a message why it didn’t work. i like it for this case - if you need to know which value exactly is null for example
g
But with empty return it also works, if you don’t need default value or null
u
We're also using a variant of
ifNotNull