3

I am trying to understand the getOrElse() function of arrow-kt. I want to create a simple function that takes a list of strings, filters them, and returns the first matching value as an Option<String>. I created the following trivial example:

fun filterStrings(incoming: List<String>): Option<String> {
    val result = incoming.filter {
        it.contains('e')
    }.firstOrNone().getOrElse {
        incoming.filter {
            it.contains('b')
        }.firstOrNone()
    }

    return result
}

@Test
fun `test filter strings`() {
    val values = listOf("aad", "aba", "cdc", "bbb")
    val result = filterStrings(values)
    assertThat(result.isSome()).isTrue()
    assertThat(result.getOrBlank()).isEqualTo("aba")
}

In this example, the code will fail to compile since result is being smartcast to Any. Why is the result variable being smartcast to Any instead of Options<String>? Is there a way to work around this without doing an unsafe cast? I am aware that there are better ways to write a function like this, but I'm trying to get a handle on the fundamentals of what is happening.

1 Answer 1

2

getOrElse unwraps an Option and returns its value, respectively the default you provided when the value is missing.

So the return type of getOrElse is the union of the Option's type - here String - and the type of the default value - here Option<String>. Their nearest common supertype is Any, so that's the type of the return value here.

The documentation describes it as follows:

Returns the option's value if the option is nonempty, otherwise return the result of evaluating default.

Sign up to request clarification or add additional context in comments.

11 Comments

We already covered that in the other question of yours. In short: There previously was orElse, but it was deemed too niche to warrant a dedicated function and was removed. The official recommendation is to use recover { alternative().bind() } instead. But as I said, all of that was already answered in the other question. What else do you want to know?
@pbuchheit But what Slaw wrote is the same that I wrote as an answer to your previous question. That shouldn't have been new to you... I'm a bit confused.
@pbuchheit You say bind() wasn't necessary, but if that was the case then the original getOrElse would be working for you. The bind() call is inside the recover { ... } block. The recover function returns an Option. The bind() function essentially "unwraps" the inner Option (returned by the second call to firstOrNone) so that recover returns Option<String> instead of Option<Option<String>>.
@pbuchheit Also, I recommend incoming.firstOrNone { it.contains(...) } instead of incoming.filter { it.contains(...) }.firstOrNone(). The latter is likely to perform unnecessary work and creates an unnecessary intermediate list.
After I switched over to using firstOrNone() and bind() I got it to work. Thanks you for your help.
@pbuchheit You seem to have forgotten that you already got that very same answer on your previous question: stackoverflow.com/a/79823950/6216216 -- Why did you ask the same here again in the comments, and why didn't you accept the answer over there?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.