melatonina
03/15/2021, 9:07 AMfun f(a: A, b: B): T = TODO()
fun g(a: A?, b: B?) {
val x = f(a?, b?)
}
where x
has type T?
and is null
if either a or b or both are null
, instead of
fun g(a: A?, b: B?) {
val x = a?.let { actualA -> b?.let { actualB -> f(actualA, actualB) } }
}
or
fun g(a: A?, b: B?) {
val x = if (a != null && b != null) f(a, b) else null
}
which would not work if a
or b
were var
, or
fun g(a: A?, b: B?) {
val x = a?.let { b?.let { f(a, b) } }
}
which is a bit silly and would not work if a
or b
were var
.
Not groundbreaking but I often would like it.kioba
03/15/2021, 3:29 PMArrow
library has a solution for this called nullable binding
https://github.com/arrow-kt/arrow/blob/85862e5629f19c1858891904044a864308d81ebe/arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/core/computations/nullable.kt
Usage:
nullable {
val aa = a.bind()
val bb = b.bind()
f(aa, bb)
} // if a or b is null shortcircuit to null, if both non null returns `f(aa, bb)`
melatonina
03/15/2021, 9:09 PMkioba
03/15/2021, 9:33 PMarrow
you can roll your own implementation:
@OptIn(ExperimentalContracts::class)
inline fun <V> nullable(crossinline block: NullableBinding.() -> V): V? {
contract {
callsInPlace(block, EXACTLY_ONCE)
}
return try {
with(NullableBinding) { block() }
} catch (ex: BindException) {
null
}
}
internal object BindException : Exception()
object NullableBinding {
fun <V> V?.bind(): V = this ?: throw BindException
}
example:
val a: Int? = 1
val b: String? = "b"
fun f(i: Int, ii: String) = i.toString() + ii
val s = nullable {
val aa = a.bind()
val bb = b.bind()
f(aa, bb)
}
val z = nullable {
val a: Int = null.bind()
val b: String = "S".bind()
f(a,b)
}
println(s)
println(z)
> 1b
> null
kioba
03/15/2021, 9:39 PMThe purpose is saving nested let expressions when we want an expression to be be null if any one of a set variables on which it depends are null:The issue with this is at the moment passing null to a non nullable argument is a compiler error. removing this could end up with a lot of bugs in codebases where people accidentally passing nullable values to non nullable arguments.
uli
03/16/2021, 12:26 PMa?.let { f(it) }
which is quite a bit harder to read. Especially as naming the lambda parameter a
will yield a warningmelatonina
03/17/2021, 12:33 PMfun f(a: A, b: B): T = TODO()
fun g(a: A?, b: B?) {
val x = f(a?, b?)
}
Note the ?
after a
and b
in the call to f
.
Otherwise the compiler would not generate the null-coalescing code and emit an error, as you are attempting to pass a nullable value to a non-nullable parameter.melatonina
03/17/2021, 5:54 PMa?.let{ f(a) }
(only when a
is not a var
, though) but I find it silly and I'd prefer writing f(a?)
, which would also be more general.kioba
03/18/2021, 10:59 AM?
in the proposal. You are right this would eliminate the ambiguity.melatonina
03/23/2021, 9:19 PMHullaballoonatic
03/31/2021, 11:19 PMephemient
06/04/2021, 2:33 PMval x = run {
f(a ?: return@run null, b ?: return@run null)
}