I have a list and I called firstOption on it, and was getting None when I thought I shouldn't be. I'd expect a function called firstOption that takes a predicate and is called on a Foldable to iterate through the foldable and return the first item matching the predicate or None if no items match, but instead I see here that it only checks the first element of the foldable.
Is this intended? If so, what's the usual use case?
Thanks!
I'd expect a function called firstOption that takes a predicate and is called on a Foldable to iterate through the foldable and return the first item matching the predicate or None if no items match
I wouldn't expect that because I think it matches the standard library's Iterable<T>.firstOrNull(). See here.
I think that's exactly what the standard library does:
Returns the first element matching the given predicate, or null if element was not found.
This is how I have it implemented right now, with a toOption() call tacked on the end.
Just in case, what you want is called find. I agree that sharing the same signature, having different behavior is weird, although it's documented what it does.
This is a specialized get I believe we inherited from funktionale.
Is it documented? I'm using 0.0.10 and IDE hover showed no documentation and click-through didn't work at all, as I ended up in a recursive cycle in a file called ListKFoldable.kt (package arrow.core.extensions.list.foldable, which does not seem to appear on GitHub anymore), here:
@JvmName("firstOption")
@Suppress(
"UNCHECKED_CAST",
"USELESS_CAST",
"EXTENSION_SHADOWED_BY_MEMBER",
"UNUSED_PARAMETER"
)
fun <A> List<A>.firstOption(): Option<A> = arrow.core.extensions.list.foldable.List.foldable().run {
arrow.core.ListK(this@firstOption).firstOption<A>() as arrow.core.Option<A>
}
@JvmName("firstOption")
@Suppress(
"UNCHECKED_CAST",
"USELESS_CAST",
"EXTENSION_SHADOWED_BY_MEMBER",
"UNUSED_PARAMETER"
)
fun <A> List<A>.firstOption(arg1: Function1<A, Boolean>): Option<A> =
arrow.core.extensions.list.foldable.List.foldable().run {
arrow.core.ListK(this@firstOption).firstOption<A>(arg1) as arrow.core.Option<A>
}
So this behavior as encountered was completely unexpected. I had to debug by printing a stack trace from the invoked closure.
You're right. The docs aren't copied into code generated by kapt, so that's lost. Here's the original function: https://github.com/arrow-kt/arrow/blob/master/modules/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Foldable.kt#L210-L214
To be clear ListKFoldable.kt is generated, so it is never on github.
I opened https://github.com/arrow-kt/arrow-core/issues/22 a few days ago (forgot to check existing issues), imo changing to the behaviour of the stdlib is the best move.
https://github.com/arrow-kt/arrow-core/pull/32 clarified this. firstOption behaves now like the standard lib firstOrNull. It is also deprecated and renamed to firstOrNone and it's an alias for find.
Most helpful comment
To be clear
ListKFoldable.ktis generated, so it is never on github.