Gavin Ray
11/30/2022, 8:08 PM?.let { } ?: emptyList()
that lets you place the default value at the beginning of the .let
statement?
One of my few complaints with Kotlin is that it becomes hard to read in cases like this:
val foo = maybe?.null?.let {
// really long series of statements
} ?: someDefault()
I'd love something like:
val foo = maybe?.null?.letWith(someDefault) {}
ephemient
11/30/2022, 8:10 PMvalue ?: orElse()
only evaluates orElse()
when `value == null`; your letWith
extension won't do that, unless it takes multiple lambdas, which is not very ergonomicGavin Ray
11/30/2022, 8:16 PMGavin Ray
11/30/2022, 8:17 PMinline fun <T> T?.withDefault(default: () -> T): T = this ?: default()
fun <T> T?.withDefault(default: T): T = this ?: default
Something like this ought to do it?ephemient
11/30/2022, 8:19 PMletWith
ephemient
11/30/2022, 8:21 PMGavin Ray
11/30/2022, 8:23 PMGavin Ray
11/30/2022, 8:24 PMephemient
11/30/2022, 8:24 PMGavin Ray
11/30/2022, 8:25 PMGavin Ray
11/30/2022, 8:25 PMGavin Ray
11/30/2022, 8:26 PMGavin Ray
11/30/2022, 8:28 PMinline fun <T, R> T?.letWith(default: R, block: (T) -> R): R = this?.let(block) ?: default
val maybeNullStr: String? = null
val letWithExample = "foo".letWith("bar") { it.toUpperCase() } // "FOO"
val letWithExample2 = maybeNullStr.letWith("bar") { it.toUpperCase() } // "bar"
ephemient
11/30/2022, 8:31 PMinline fun <T : Any, R> T?.let(ifNull: () -> R, ifNotNull: (T) -> R): R = if (this == null) ifNull() else ifNotNull(this)
maybeNullStr.let(
ifNull = { "foo" },
ifNotNull = { it.uppercase() }
)
that would preserve laziness of the default, and does have some precedent in stdlib (.associateBy()
has an overload taking multiple lambdas), but I don't think it's worth a standard additionephemient
11/30/2022, 8:34 PMGavin Ray
11/30/2022, 8:38 PMAny.prettyPrint()
, extension functions are a lot of magic.ephemient
11/30/2022, 8:47 PMmaybeNullStr.let {
it ?: return@let "foo"
it.uppercase()
}
Gavin Ray
11/30/2022, 8:52 PMGavin Ray
11/30/2022, 8:52 PMKevin Del Castillo
11/30/2022, 10:24 PM?.let {} ?: emptyList()
you can use .orEmpty()
, for other values that don't have it you'd have to define your own extension, we have one in our project called .ifNull { }
, it's useful when you're chaining operations:
inline fun <T> T?.ifNull(block: () -> T): T = this ?: block()
ephemient
11/30/2022, 10:34 PMwithDefault
above by a different name - doesn't achieve the reordering OP was looking forKevin Del Castillo
11/30/2022, 10:37 PMKevin Del Castillo
11/30/2022, 10:40 PMval foo = maybe
?.null
?.let {
// ...
}
.ifNull {
someDefault()
}
As let
is usually used for small pieces of code, if OP is writing a considerable amount of statements in there they may want to consider moving that logic to a function.ephemient
11/30/2022, 10:41 PMKevin Del Castillo
11/30/2022, 10:42 PMephemient
11/30/2022, 10:43 PMephemient
11/30/2022, 10:43 PMephemient
11/30/2022, 10:46 PMGavin Ray
11/30/2022, 10:51 PMAsThis is a fair point to be honest, the argument could be made that the ~50 lines in theis usually used for small pieces of code, if OP is writing a considerable amount of statements in there they may want to consider moving that logic to a function.let
let
block ought to be extractedJoffrey
11/30/2022, 11:14 PMJoffrey
11/30/2022, 11:16 PMAdrijan Rogan
12/02/2022, 8:50 AMJoffrey
12/02/2022, 8:52 AMif
and else
clause should IMO be very short too for clarity, so yes this would imply extracting a function too for each side depending on the codeAdrijan Rogan
12/02/2022, 8:54 AMfun f(x: X?): List<Y> {
if (x == null)
return emptyList()
// use non-null x here
}
Or move the if check to before calling this functionJoffrey
12/02/2022, 8:55 AM// some code
val list = x?.let { makeAListOutOf(it) ?: emptyList()
fun makeAListOutOf(x: X): List<Y> {
// use non-null x here
}
But depending on the meaning of the function, you might want to make it an extensions and it's even easier to deal with nulls then:
// some code
val list = x?.transformSomehow() ?: emptyList()
fun X.transformSomehow(): List<Y> {
// use non-null x here
}
Joffrey
12/02/2022, 8:57 AMmap
/ filter
and the likes, I also find it clearer if the body of such functions are single expressions (usually a function call, often an extension function call that makes it read nicely like english, for instance things.map { it.toSomeOtherThing() }
)