I'm having a lot of difficulty figuring out a good way to coordinate using RxJava along with the arrow-kt Either
and Option
types. I have two methods that both return Single<Either<ApiError, Option>
class Foo(val qux: Option<Qux>)
class Bar
class Qux
class ApiError
fun loadFoo(): Single<Either<ApiError, Option<Foo>>> {
...
}
fun loadBar(qux: Qux): Single<Either<ApiError, Option<Bar>>> {
...
}
The goal is to return the result of loadBar(Qux)
in an RxJava Single
as the type Either<ApiError, Option<Bar>>
.
The complication comes from the fact that the qux
parameter required by loadBar()
is retrieved from the data emitted by the Single
returned by loadFoo()
(Qux
is a property of Foo
with the type Option<Qux>
).
Desired outcome:
- Any
ApiError
s that occur get passed to theSingle
's subscriber inEither.Left
- If both
loadFoo()
andloadBar()
returnSome
, that value should be returned in the composedSingle
asEither.Right
- If either
loadFoo()
orloadBar()
returnNone
, the expected result isEither.Right(None)
I tried a couple things. This first example works, but the resulting code is hard to read because of a bunch of nested folds, as well as intermixing of RxJava and Either/Option
operators.
fun loadBarFromMaybeFoo(maybeFoo: Option<Foo>): Single<Either<ApiError, Option<Bar>>> {
return maybeFoo.flatMap { foo -> foo.qux }
.map { qux -> loadBar(qux) }
.getOrElse { Single.just(Either.Right(Option.empty())) }
}
fun doStuffToGetBar(): Single<Either<ApiError, Option<Bar>>> {
return loadFoo()
.flatMap { maybeFooOrError ->
maybeFooOrError.fold(
{ error -> Single.just(Either.Left(error)) },
{ maybeFoo -> loadBarFromMaybeFoo(maybeFoo) }
)
}
}
The second thing I tried was to use arrow's rxjava observable comprehensions. But couldn't quite figure out how to get this to return Single<Either<ApiError, Option>
in the end.
fun doStuffToGetBar(): Single<Either<ApiError, Option<Bar>>> {
return SingleK.monadDefer().bindingCatch {
val maybeFooOrError: Either<ApiError, Option<Foo>> = loadFoo().k().bind()
val maybeQuxOrError: Either<ApiError, Option<Qux>> = maybeFooOrError.map { maybeFoo ->
maybeFoo.flatMap { it.qux }
}
// return type is Either<ApiError, Option<Either<ApiError, Option<Bar>>>>
// desired return type is Either<ApiError, Option<Bar>>
maybeQuxOrError.map { maybeQux ->
maybeQux.map { qux ->
loadBar(qux).k().bind() // this part doesn't seem good
}
}
}.value()
}
Any help/advice on how to solve this or restructure the data types to make it easier would be much appreciated! Still pretty new to many functional programming concepts.