Reputation: 353
I have a List<T?>
containing null values (which, I suppose, is not forbidden). If one element of this list is null, I want the entire list to be null (what Haskell people call sequence
). The following pseudocode demonstrates what I want to do:
fun <T> sequence(a : List<T?>) : List<T>? {
return
a.fold(
listOf(),
{
prevArray, element ->
if(element == null) null else prevArray + element
})
}
This is pseudocode because the compiler complains that Null can not be a value of a non-null type kotlin.collections.List<T>
.
What's the idiomatic way to express what I want to in Kotlin? Using Java's Optional
type, this is at least compilable:
fun <T> sequence(a : List<T?>) : Optional<List<T>> {
return
a.fold(
Optional.of(listOf()),
{
prevArray, element ->
if(element == null) Optional.empty<List<T>>() else Optional.of(prevArray + element)
})
}
But Kotlin has many operators and functionalities regarding null handling, so I thought using null directly would be more idiomatic.
Upvotes: 7
Views: 9462
Reputation: 21
Im fan of this way:
fun <T> isNull(value: T?): Boolean {
return value == null
}
fun <T> containsNoNulls(list: List<T?>): Boolean {
return list.none(::isNull)
}
fun <T> sequence(a: List<T?>): List<T>? {
return a.takeIf(::containsNoNulls)?.filterNotNull()
}
Upvotes: 1
Reputation: 399
Using Konad library you can now do the following:
fun <T> sequence(a : List<T?>) : List<T>? = a.flatten()?.toList()
Upvotes: 0
Reputation: 32776
You can use a non-local return to return from the sequence
function:
fun <T> sequence(a: List<T?>): List<T>? {
return a.fold(listOf()) {
prevArray, element ->
if (element == null) return null else prevArray + element
}
}
However, I would solve the problem you described with a simple if
-expression, to prevent lots of list allocations which happen because the list addition creates a new list backed by array for each element. The unchecked cast warning is suppressed below because the compiler cannot figure out that the list does not contain nulls at that point, although we can clearly see that is the case:
fun <T> sequence(a: List<T?>): List<T>? {
@Suppress("UNCHECKED_CAST")
return if (a.any { it == null }) null else a as List<T>
}
Upvotes: 14