What exactly is the purpose of functions like `fir...
# language-evolution
n
What exactly is the purpose of functions like
firstNotNullOfOrNull
? I have seen an increasing number of these functions appearing and they always strike me as excessive and obtuse - this one in particular still does not make sense to me, even after some soul seeking. It's a combination of
firstOrNull
and
mapNotNull
, the latter of which is itself a "convenience" for
filterNotNull().map { ... }
. Where does it end? Should we have
firstNotNullOfOrNullIsInstanceOf
? I feel like I'm missing something here. To me, seeing this in code explains a lot more conceptually, and I don't see what's "wrong" with it:
Copy code
data.map { String.toDoubleOrNull(it) }.filterNotNull().firstOrNull()
e
2 list creations when 0 are needed
r
And it doesn't short circuit
n
Surely the solution to both of these problems is to use a
Sequence
? There's just no way to avoid excessive list creations for all possible combinations of mapping, filtering, finding, selecting, etc. when using Iterables. IMO, these methods create a bit of an anti-pattern as they try to support more and more niche use cases. If performance is key, a
Sequence
should be used.
e
Sequence can't short-circuit in the way that the inline Iterable functions can:
data.firstNotNullOfOrNull { if (it == 5) return "special case" else ... }
and like Java streams, there is an overhead to the laziness of Sequence. for small collections, Iterable is faster, even with the extra lists created. https://medium.com/@ajalt/benchmarking-kotlin-sequences-e06d8bb4011c
n
That's a fair point - though as the linked article mentions, all of these operations are already well optimized. I'm still not sure what the end goal here is, though: should a function exist as a shortcut for every conceivable combination of actions?
e
the shortcutting behavior of
.firstNotNullOfOrNull()
is different than
.mapNotNull().firstOrNull()
, and JB uses it enough to deem it worthy of addition to stdlib. similarly, at least in my code,
.mapNotNull()
is definitely common enough that it's worth having a shortcut for it over
.filter().map()
or
.map().filterNotNull()
. if it didn't exist, we'd have written our own, as we have with some things like
.applyIf()
etc.
some more were added in 1.5, but I do wish they'd flesh out more consistent
*indexed*()
iterable methods too (e.g. there's currently no
.allIndexed()
,
.joinToStringIndexed()
, etc.)
I personally don't find the
.reduce()
or
.foldRight()
methods useful -
.fold()
and
.reduceOrNull()
are enough - but it's also not harming anything