dnowak
02/03/2020, 9:35 AMIO<Either<DomainError, Value>>. I would like to retry not only on Exception reported within IO context but also on logical DomainError reported within contained Either.
The only thing connected to retries I found is https://github.com/luisdeltoro/arrow-effects-retrysimon.vergauwen
02/03/2020, 10:45 AMSchedule which works for any F. It allows you to retry based on the Exception or the value produced by the F. So you can also use Schedule to build complex retries based of the E in your Either.
https://next.arrow-kt.io/docs/apidocs/arrow-fx/arrow.fx/-schedule/simon.vergauwen
02/03/2020, 10:47 AME to IO so you can also track custom domain errors within IO without having to nest Either or rely on EitherT for specialised syntax.simon.vergauwen
02/03/2020, 10:49 AMSchedule is available on 0.10.5-SNAPSHOT, which will be released in the following weeks. We’re finishing the release, and adding a deprecation cycle for breaking signatures when adding E to IO.simon.vergauwen
02/03/2020, 10:57 AMTimer typeclass which has a proper cancelable implmentation. The implementation here based on KotlinX is unsafe and uncancelable 😉Jannis
02/03/2020, 1:48 PMSchedule work with IO<Either<DomainError, Value>> prior to 0.11 Bifunctor IO you can always do EitherT(ioWithEither).retry(EitherT.monadError(), Timer(EitherT.concurrent(IO.concurrent()))), schedule) and if you want to get your IO<Either<DomainError, Value>> back just call value() on the resulting IO. (That needs a dependency on arrow-fx-mtl).
Also take a look at simons example below, that works without EitherTsimon.vergauwen
02/03/2020, 1:49 PMSchedule also supports retrying based on the value, right? So you can already retry based on encountering certain Left values, no?Jannis
02/03/2020, 1:49 PMJannis
02/03/2020, 1:49 PMJannis
02/03/2020, 1:53 PMretry when given a MonadError + Timer instead of Concurrent is polymorphic in E and will take the errors from the MonadError instance. But `IO`'s current MonadError instance is fixed to Throwable right? So you would need either a custom MonadError instance, or just use EitherTJannis
02/03/2020, 1:54 PMConcurrent ...simon.vergauwen
02/03/2020, 1:59 PMrepeat to retry values. You can retry values by simply composing flatMap, right?
fun <E, A> retryDomainOnLeftSchedule(): Schedule<ForIO, Either<E, A>, Either<E, A>> =
Schedule.withMonad(IO.monad()) {
doWhile<Either<E, A>> { it is Either.Left }
}
suspend fun main(): Unit =
IO.effect { println("Hello World!") }
.map { it.left() }
.repeat(IO.concurrent(), retryDomainOnLeftSchedule())
.unit()
.suspended()
This retries on Left until Right is found, doWhile<Either<E, A>> { it is Either.Left }.
So this will print “Hello World” foreversimon.vergauwen
02/03/2020, 2:00 PMdoWhile + repeat is a perfect retry mechanism for effectful values.Jannis
02/03/2020, 2:00 PMJannis
02/03/2020, 2:01 PMIO but neither would EitherT so that's fine I guesssimon.vergauwen
02/03/2020, 2:02 PMIO? And so does EitherT if stacked with IOsimon.vergauwen
02/03/2020, 2:02 PMsimon.vergauwen
02/03/2020, 2:03 PMJannis
02/03/2020, 2:04 PMYou should still take care of exceptions separate from retrying your domain if that is what you meant.Yes exactly.
retry with IO.concurrent will only be triggered if there is an error inside IO for example. retry with EitherT.monadError will only trigger on Kind<F, Left...> etcJannis
02/03/2020, 2:05 PMrepeat you need to handle errors manually, which given that they are already in IO<Either<L, R>> form is probably already a thingsimon.vergauwen
02/03/2020, 2:09 PME side in MonadError is always fixed to Throwable.simon.vergauwen
02/03/2020, 2:09 PMThrowable, so all effect typeclasses have to deal with that. Hence MonadThrowJannis
02/03/2020, 2:14 PMretry and repeat take MonadError + Timer instead, so that domain errors can be used. The concurrent overload is more of a convience thing. This needs better documentation when 0.11 comes around!Jannis
02/03/2020, 2:18 PMConcurrent<F> is always split into MonadError<F, Throwable> and Timer<F> and the main methods that implement everything work on MonadError and Timer and not Concurrentsimon.vergauwen
02/03/2020, 2:19 PMME + Timer for that reasondnowak
02/03/2020, 11:11 PM