I'm surprised that there doesn't seem to be a retr...
# arrow
d
I'm surprised that there doesn't seem to be a retry for Either.Left, only for Throwable in Arrow resilience...? When using the retrofit Arrow adapter, we get Either.Left... why should I have to rethrow it to catch it again?
a
d
Boy, Intellij is really terrible at finding top-level functions then... I even imported that whole package... what version is that @Alejandro Serrano.Mena?
It doesn't seem to exist in my current version
a
it should be there in 1.2.4
d
Yeah, it is, thanks! I had another funny crash because of resilience in the last version, maybe this version fixes that too...
@Alejandro Serrano.Mena As soon as I use resilience, I get this compile error (even in 1.2.4):
Copy code
error: cannot access SearchSuggestionsViewModel
    public abstract ViewModel binds(SearchSuggestionsViewModel vm);
                                    ^
  bad class file: /home/.../app/build/tmp/kotlin-classes/debug/.../searchScreens/SearchSuggestionsViewModel.class
    undeclared type variable: Input
    Please remove or make sure it appears in the correct subdirectory of the classpath.
As soon as I comment out the resilience code, everything compiles normally.
I'm using:
Copy code
private val recur2 = Schedule.recurs<CallError>(3)

...
recur2.retryEither {
   searchApi.getSuggestions(...) // returns an Either<CallError, List<String>>
}
a
do you mind reporting this? we use to have those errors in earlier versions, and I thought they were all gone 😭
a
thank you!
d
I guess there's no workarounds... ?
a
not without recompiling arrow, unfortunately; in the meanwhile maybe you can copy the implementation of
retryEither
?
d
It seems like even leaving
private val recur2 = Schedule.recurs<CallError>(3)
in the code causes the error 😵‍💫...
I guess I'll have to wait for the next version, that's hopefully coming out soon 😊? It seems that there haven't been many versions coming out lately... but I'm sure you're all on your toes working towards version 2... 😁.
Wow, I copied the whole Schedule file into my project, and I'm still getting that error...!
a
the "trick" is to make the type parameters to
Schedule
in the extension funcion to coincide exactly with those of the original
Schedule
definition
d
I'm not sure what you mean... how could I do that in my case?
I have to change the code I copied over into my project? Or the code that I wrote above using it @Alejandro Serrano.Mena?
a
instead of
Error
, name that type parameter
Input
(this is the original name in
Schedule<Input, Output>
)
d
I did this and I still get the same error 🤔:
Copy code
/**
 * Retries [action] using any [Throwable] that occurred as the input to the [Schedule].
 * It will throw the last exception if the [Schedule] is exhausted, and ignores the output of the [Schedule].
 */
public suspend fun <A> Schedule<Throwable, *>.retry(action: suspend () -> A): A =
    retryOrElse(action) { e, _ -> throw e }

/**
 * Retries [action] using any [Throwable] that occurred as the input to the [Schedule].
 * If the [Schedule] is exhausted,
 * it will invoke [orElse] with the last exception and the output of the [Schedule] to produce a fallback [Input] value.
 */
public suspend fun <Input, Output> Schedule<Throwable, Output>.retryOrElse(
    action: suspend () -> Input,
    orElse: suspend (Throwable, Output) -> Input
): Input = retryOrElseEither(action, orElse).merge()

/**
 * Retries [action] using any [Throwable] that occurred as the input to the [Schedule].
 * If the [Schedule] is exhausted,
 * it will invoke [orElse] with the last exception and the output of the [Schedule] to produce a fallback value of [A].
 * Returns [Either] with the fallback value if the [Schedule] is exhausted, or the successful result of [action].
 */
public suspend fun <Input, Output, A> Schedule<Throwable, Output>.retryOrElseEither(
    action: suspend () -> Input,
    orElse: suspend (Throwable, Output) -> A
): Either<A, Input> {
    var step: ScheduleStep<Throwable, Output> = step

    while (true) {
        currentCoroutineContext().ensureActive()
        try {
            return Either.Right(action.invoke())
        } catch (e: Throwable) {
            when (val decision = step(e)) {
                is Continue -> {
                    if (decision.delay != ZERO) delay(decision.delay)
                    step = decision.step
                }

                is Done -> return Either.Left(orElse(e.nonFatalOrThrow(), decision.output))
            }
        }
    }
}

/**
 * Retries [action] using any [Error] that occurred as the input to the [Schedule].
 * It will return the last [Error] if the [Schedule] is exhausted, and ignores the output of the [Schedule].
 */
public suspend inline fun <Input, Result, Output> Schedule<Input, Output>.retryRaise(
    @BuilderInference action: Raise<Input>.() -> Result,
): Either<Input, Result> = either {
    retry(this@retryRaise, action)
}

/**
 * Retries [action] using any [Error] that occurred as the input to the [Schedule].
 * It will return the last [Error] if the [Schedule] is exhausted, and ignores the output of the [Schedule].
 */
public suspend inline fun <Input, Result, Output> Schedule<Input, Output>.retryEither(
    @BuilderInference action: () -> Either<Input, Result>,
): Either<Input, Result> = retryRaise {
    action().bind()
}

/**
 * Retries [action] using any [Error] that occurred as the input to the [Schedule].
 * It will return the last [Error] if the [Schedule] is exhausted, and ignores the output of the [Schedule].
 */
public suspend inline fun <Input, Result, Output> Raise<Input>.retry(
    schedule: Schedule<Input, Output>,
    @BuilderInference action: Raise<Input>.() -> Result,
): Result {
    var step: ScheduleStep<Input, Output> = schedule.step

    while (true) {
        currentCoroutineContext().ensureActive()
        fold(
            action,
            recover = { error ->
                when (val decision = step(error)) {
                    is Continue -> {
                        if (decision.delay != ZERO) delay(decision.delay)
                        step = decision.step
                    }

                    is Done -> raise(error)
                }
            },
            transform = { result ->
                return result
            },
        )
    }
}
a
may you remove the
Input
type parameters in the functions where they are not used?
d
they seem to always be used.
Note that Schedule has in and out in it's declaration... maybe that has something to do with this:
Copy code
@JvmInline
public value class Schedule<in Input, out Output>(public val step: ScheduleStep<Input, Output>)
I commented out everything except for the recurs function and Decision's Done and Continue (even retryXXX), and I just left the declaration of the schedule in my code (no retry block) and it still fails... 🤷🏼‍♂️ @Alejandro Serrano.Mena
There seems to be another open issue about this: https://github.com/arrow-kt/arrow/issues/3325
It seems like the problem is that Schedule is a value class... I removed
value
and
@JvmInline
and it works...
I updated the issue with my findings.
a
thank you 🙂
👍🏼 1