Philipp Middendorf
Philipp Middendorf

Reputation: 353

List containing nullable values to a nullable List in Kotlin

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

Answers (3)

Arjen Keller
Arjen Keller

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

Luca Piccinelli
Luca Piccinelli

Reputation: 399

Using Konad library you can now do the following:

fun <T> sequence(a : List<T?>) : List<T>? = a.flatten()?.toList()

Upvotes: 0

Alexander Udalov
Alexander Udalov

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

Related Questions