Why doesn't firstOf exist?

We have extensions functions:
first, firstOrNull, firstNotNullOf, firstNotNullOfOrNull
but not:
firstOf, firstOfOrNull

Why is that the case?

 


For example with my implementation of firstOfOrNull like:

inline fun <T, R> Iterable<T>.firstOfOrNull(
    transform: (T) -> R,
    predicate: (R) -> Boolean
): R? {
    for (element in this) {
        val result = transform(element)
        if (predicate(result)) {
            return result
        }
    }
    return null
}

Using it in an example situation, compared to doing the same with firstOrNull and firstNotNullOfOrNull:

val sortedValueList = listOf("1", "2", "3", "4", "5")
fun transformation(s: String) = s.toInt() * 10

listOf(
    { i: Int -> i > 25 },
    { i: Int -> i > 100 },
).forEach { predicate ->
    listOf(
        //0 firstOfOrNull
        sortedValueList.firstOfOrNull(::transformation, predicate),

        //1 firstOrNull (does 1 extra transformation compared to the other two)
        sortedValueList.firstOrNull {
            predicate(transformation(it))
        }.let { it?.let { transformation(it) } },

        //2 firstNotNullOfOrNull
        sortedValueList.firstNotNullOfOrNull {
            val t = transformation(it)
            if (predicate(t)) t else null
        }

    ).forEachIndexed { i, it -> println("$i: $it") }
}

(Outputs)

0: 30
1: 30
2: 30
0: null
1: null
2: null

I think there is a point in having the firstOfOrNull function as it is cleaner.

Or did I miss something? :woozy_face:
Thanks for reading through!

Is it any different than list.map(...).first(...)?

(Kotlin 1.9.0)

public inline fun <T> Iterable<T>.firstOrNull(predicate: (T) -> Boolean): T? {
    for (element in this) if (predicate(element)) return element
    return null
}

@kotlin.internal.InlineOnly
public inline fun <T, R : Any> Iterable<T>.firstNotNullOfOrNull(transform: (T) -> R?): R? {
    for (element in this) {
        val result = transform(element)
        if (result != null) {
            return result
        }
    }
    return null
}

first functions would return after finding the first element

list.map(...).first(...) would perform mapping for all the entries, then find the first match

1 Like

Then: asSequence().map { ... }.first { ... }.

I see your point. I’m just not sure if it makes sense to duplicate all filtering operators like first, last, filter, takeWhile and many more just to provide lazy map+filter functionality. Not to mention combinations like e.g. flatMap+filter.

1 Like

Yeah but in that case it got me thinking why something so verbose like firstNotNullOfOrNull exists to begin with :thinking:

1 Like

Isn’t this find? find - Kotlin Programming Language

1 Like

Found this

From a purely aesthetic position, functions that take multiple lambdas in Kotlin are kind of ugly imo; having the final lambda be outside the parentheses results in a nice look for Kotlin, but if you have multiple lambdas, then at least one has to be inside parentheses and it looks weird. For example:

listOf(1, 2, 3, 4, 5)
.firstOfOrNull({ it * 2}) { it > 5 }

vs

listOf(1, 2, 3, 4, 5)
.map { it * 2 }
.firstOrNull { it > 5 }

Plus as broot said, it’s really not that much extra code to transform your collection to a sequence and then do map + firstOrNull.

yeah I agree that the ({ ... }) block looks pretty bad

lastNotNullOf and lastNotNullOfOrNull also doesn’t exist when their “first” counterparts exists, there are consistency issues here

fun <T, R: Any> Iterable<T>.lastNotNullOfOrNull(transform: (T) -> R?): R? {
    var last: R? = null
    for (element in this) {
        val result = transform(element)
        if (result != null) {
            last = result
        }
    }
    return last
}

but then similarly again, if this shouldn’t exist then why should its counterpart firstNotNullOfOrNull exist :thinking:
I can’t help but to feel like there is something I am missing, something that I didn’t consider beside the readability or aesthetic issues

Now that I’ve looked at firstNotNullOfOrNull and firstNotNullOf, I think those are kind of weird functions to have… a simple map that returns a null followed by filterNotNull and then firstOrNull would work… I guess it’s a bit shorter, but it seems like a pretty niche use case to have a built-in language function for this. Maybe a lot of the Kotlin devs need it so they added it as a standard function? Or… I think Kotlin language features are often requested by the community, yeah? Maybe there’s a ticket for adding those standard functions, which would have explanations from people as to why they wanted those functions.

Which can be avoided by converting to a sequence:

list.asSequence().map(...).first(...)

In fact I would rewrite your function as:

inline fun <T, R> Iterable<T>.firstOfOrNull(
    transform: (T) -> R,
    predicate: (R) -> Boolean
): R? =
    asSequence().map(transform).firstOrNull(predicate)

There is another alternative with firstNotNullOfOrNulll:

 inline fun <T, R> Iterable<T>.firstOfOrNull(
    transform: (T) -> R,
    predicate: (R) -> Boolean
): R? =
    firstNotNullOfOrNull { transform(it).takeIf(predicate) }   

That is the nice thing about Kotlin, if you find such a function valuable, you can add it. It is not likely to make it in stdlib because there are other ways to do it and because they generally try to have only one lambda parameter in a function call as it is more readable.

1 Like