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 EitherT
simon.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 EitherT
Jannis
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 IO
simon.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 MonadThrow
Jannis
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 Concurrent
simon.vergauwen
02/03/2020, 2:19 PMME + Timer
for that reasondnowak
02/03/2020, 11:11 PM