Anyone else feel this (`trySingle` for collections...
# language-proposals
e
Anyone else feel this (
trySingle
for collections) would be useful as part of the stdlib? Or have any alternate solutions? 🙂
Copy code
fun doSomething(customerId: Int) {
   val user = when (val result = fetchExistingUsers().trySingle { it.id == customerId }) {
      is MultipleFound -> error("Duplicate users for single ID")
      is NoneFound -> createUser()
      is Single -> result.single
   }
}
Currently implemented on my own as follows..
trySingle
is a slightly modified
singleOrNull
Copy code
sealed interface TrySingleResult<out T>

class Single<T>(
   val single: T
) : TrySingleResult<T>

object MultipleFound: TrySingleResult<Nothing>
object NoneFound: TrySingleResult<Nothing>

inline fun <T> Iterable<T>.trySingle(predicate: (T) -> Boolean): TrySingleResult<T> {
   var single: T? = null
   var found = false
   for (element in this) {
      if (predicate(element)) {
         if (found) return MultipleFound
         single = element
         found = true
      }
   }
   if (!found) return NoneFound

   @Suppress("UNCHECKED_CAST")
   return Single(single as T)
}
e
this is something that Scala's pattern matching would work on,
Copy code
fetchExistingUsers().filter { _.id == customerId } match {
    List(singleId) => singleId
    List(_, _*) => error(...)
    _ => error(...)
}
there isn't a finished design for what it would look like in Kotlin, but https://youtrack.jetbrains.com/issue/KT-186 may be coming in a future version of Kotlin
👀 2
h
For this specific use case you could use
filter
and check its size.
e
Thanks @ephemient, that looks very interesting and the scala approach seems elegant and applicable to a wide variety of use-cases 🙂 @hfhbd I'm aware, but it breaks the handling of all branches into multiple statements. The
when
-clause forces you to deal with all branches immediately (multiple, single and none). I think it's generally preferable
e
Copy code
when (result.size) { 0 -> ; 1 -> ; _ -> }
1
not the most elegant but that would work as well
h
Copy code
fun doSomething(customerId: Int) {
   val user = fetchExistingUsers().filter { it.id == customerId }.let {
      when(it.size) {
      0 -> createUser()
      1 -> it.single()
      else -> error("Duplicate users for single ID")
   }
}
e
Another alternative:
trySingle(predicate: (T) -> Boolean): Result<T?>
using Kotlin's built-in
Result
-type.. Then you can do something like:
Copy code
fun doSomething(customerId: Int) {
   val user = fetchExistingUsers()
      .trySingle { it.id == customerId }
      .getOrElse { println("Multiple found") }
      ?: createUser()
}
@hfhbd nice, clever to use let to scope the result before using
when
🙂