Arrow: What should List<A>.firstOption { A -> Boolean } return?

Created on 24 Sep 2019  路  8Comments  路  Source: arrow-kt/arrow

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!

Most helpful comment

To be clear ListKFoldable.kt is generated, so it is never on github.

All 8 comments

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lgtout picture lgtout  路  4Comments

1Jajen1 picture 1Jajen1  路  3Comments

pakoito picture pakoito  路  4Comments

gortiz picture gortiz  路  3Comments

vejeta picture vejeta  路  3Comments