What is the idiomatic Kotlin way to translate the ...
# announcements
t
What is the idiomatic Kotlin way to translate the following Swift snippet:
if let foo = someOptionalExpression, let bar = optionalExpression(foo) {
// work with foo AND bar
}
else {
// deal with nil/null case
}
z
Copy code
someOptionalExpression?.let { foo ->
  optionalExpression(foo)?.let { bar ->
    // work with foo and bar
  }
} ?: // deal with null case
👍 1
t
@Zach Klippenstein (he/him) [MOD] don't I have to be careful with what I return there? Isn't it the case that if
// work with foo and bar
returns
null
then the handler will fire?
z
yes
if that’s an issue, then i would just use variables:
Copy code
val foo = someOptionalExpression
val bar = foo?.let { optionalExpression(it) }
if (foo != null && bar != null) {
  …
} else {
  …
}
t
Isn’t it the case that if
// work with foo and bar
returns
null
then the handler will fire?
I believe that is not correct, as there’s nothing that handles
optionalExpression(foo)
being evaluated to
null
You would have to do:
Copy code
expressionA?.let { foo ->
  expressionB(foo)?.let { bar ->
    expressionC
  } ?: // deal with expressionB or C evaluating to null
} ?: // deal with expressionA evaliating to null
But perhaps you only have to worry about expressionC here if you’re attempting to return it? Returns in closures are a bit tricky in Kotlin so you do have to be careful
t
That's what I thought @Tim Malseed. The single level ?.let {} ?: run {} works pretty effectively for simple
if let...
expressions, but I've struggled with how to use the same pattern when there are multiple conditions, especially when the conditions are cumulative
t
There’s nothing that maps particularly well to your swift function
You have to get a little bit messy with nested if statements, use old-fashioned vars, or get really clever with
takeIf()
I don’t think there’s an idiomatic way to do this in Kotlin yet
When the conditions are not cumulative, I use:
Copy code
inline fun <T1 : Any, T2 : Any, R : Any> biLet(p1: T1?, p2: T2?, block: (T1, T2) -> R?): R? {
    return if (p1 != null && p2 != null) block(p1, p2) else null
}
Copy code
biLet(optionalA, optionalb) { a, b -> 

}
t
actually, I think the nested version DOES work.
Copy code
fun <T>optionalOne(arg:T, beNull:Boolean) : T? {
    return if (beNull) { null } else { arg }
}

fun <T>optionalTwo(arg:T, beNull:Boolean) : T? {
    return if (beNull) { null } else { arg }
}

@Test
fun testStuff() {
    this.optionalOne(2, false)?.let { one ->
        this.optionalTwo(13, true)?.let { two ->
            "RESULT IS ${one * two}".print()
        }
    } ?: run {
        "FAILURE BLOCK".print()
    }
}
t
Yes, it ‘works’
But you’re not handling the case where
this.optionalTwo(13, true)
is null
t
that's true
but a return type of Unit is not the same as null return, is it?
a
I think @Zach Klippenstein (he/him) [MOD]'s way should just work? replace most inner
let
with
also
@Tim Malseed the
optionalExpression(good)
evaluating to null will cause the outer
let
to evaluate to null and thus the handler will fire
👍 1
t
I feel like you just have to test it and see. It’s never quite obvious exactly what will happen
c
This has been discussed many times, let is a sorry replacement for sure. Even Scala’s Option is superior to what we have in K, because it uses pattern-matching, so you are less likely to find the code littered with a ton of non-null assumptions and zero consideration to null cases.