Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

["Request"] Keep handleErrorWith function #3122

Open
PavlosTze opened this issue Sep 1, 2023 · 2 comments
Open

["Request"] Keep handleErrorWith function #3122

PavlosTze opened this issue Sep 1, 2023 · 2 comments

Comments

@PavlosTze
Copy link

What version are you currently using?

    // Data types and more
    implementation(platform("io.arrow-kt:arrow-stack:1.2.0"))
    implementation("io.arrow-kt:arrow-core")
    implementation("io.arrow-kt:arrow-fx-coroutines")

What would you like to see?
I can see that handleErrorWith function is deprecated, I would like to ask to keep it as is. It seems way more complex to use the recover functionality for some simple thins and I still haven't figured out how to do it in some instances e.g:

    override suspend fun getHourlyWeatherHistory(
        deviceId: String,
        date: LocalDate,
        forceUpdate: Boolean
    ): Either<Failure, List<HourlyWeather>> {
        return if (forceUpdate) {
            Timber.d("Forced update. Skipping db.")
            getHistoryFromNetwork(deviceId, date)
        } else {
            Timber.d("Non-forced update. Trying db first.")
            getHistoryFromDatabase(deviceId, date)
                .handleErrorWith {
                    getHistoryFromNetwork(deviceId, date)
                }
        }
    }

If I use the suggestion from Android Studio the getHistoryFromNetwork doesn't return an Either with recover, but returns a List<HourlyWeather>.

Also I can't make the following work with recover, compiler errors.

    override suspend fun getSearchSuggestions(
        query: String
    ): Either<Failure, List<SearchSuggestion>> {
        return addressRepository.getSearchSuggestions(query)
            .handleErrorWith {
                when (it) {
                    is CancellationError -> Either.Right(emptyList())
                    else -> Either.Left(it)
                }
            }
    }
@nomisRev
Copy link
Member

nomisRev commented Sep 4, 2023

Hey @PavlosTze,

Thanks for the bug report! First of all, let's see if we can fix your snippets (for documentation purposes)

In the first snippet, it's would need an additional bind when switching from handleErrorWith to recover.

    override suspend fun getHourlyWeatherHistory(
        deviceId: String,
        date: LocalDate,
        forceUpdate: Boolean
    ): Either<Failure, List<HourlyWeather>> {
        return if (forceUpdate) {
            Timber.d("Forced update. Skipping db.")
            getHistoryFromNetwork(deviceId, date)
        } else {
            Timber.d("Non-forced update. Trying db first.")
            getHistoryFromDatabase(deviceId, date)
                .recover {
                    getHistoryFromNetwork(deviceId, date).bind()
                }
        }
    }

In the second snippet you're returning Either<E, A>, and that would need to be changed to A.

    override suspend fun getSearchSuggestions(
        query: String
    ): Either<Failure, List<SearchSuggestion>> {
        return addressRepository.getSearchSuggestions(query)
            .recover {
                when (it) {
                    is CancellationError -> emptyList()
                    else -> raise(it)
                }
            }
    }

This way both snippets should compile, and work. Would be great if you could test them. I've been wondering a lot about recover, because there is one short-coming that is annoying with inference.

val original: Either<String, Int> = "failure".left()
val x = original.recover { -1 } // cannot infer `Left`, should be `Either<Nothing, Int>`
val y: Either<Nothing, Int> = original.recover [ -1 } // is fine because of explicit type
val z = original.recover { raise(MyError) } // is fine because of builder inference

Let's get some more opinions here, but I think we can consider keeping handleErrorWith, and perhaps also handleError because those are popular APIs and will not bloat the API or binary since they're only needed for Either.

IMO Ideally Ithe Kotlin compiler could infer Nothing for receiver lambda arguments when the receiver is unused, but perhaps we can get that feature in the future.

cc\ @raulraja @franciscodr @serras

@PavlosTze
Copy link
Author

@nomisRev The first one isn't working properly. This way, the return result of:

                .recover {
                    getHistoryFromNetwork(deviceId, date).bind()
                }

is List<HourlyWeather> based on what Android Studio says. The problem is that my original code:

                .handleErrorWith {
                    getHistoryFromNetwork(deviceId, date)
                }

returns an Either<Failure, List<HourlyWeather>> which is what I would want to. So I think in some cases the recover code will produce wrong results?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants