我很难找到一种很好的方法来与RkJava以及arrow-kt Either
和Option
类型一起使用。我有两种方法都返回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>>> {
...
}
目标是在RxJava loadBar(Qux)
中以类型Single
返回Either<ApiError, Option<Bar>>
的结果。
复杂的原因是,qux
所需的loadBar()
参数是从Single
返回的loadFoo()
发出的数据中检索的(Qux
是Foo
的属性,类型为Option<Qux>
)。
所需结果:
ApiError
都将传递给Single
中Either.Left
的订户loadFoo()
和loadBar()
都返回Some
,则该值应以Single
的形式在组成的Either.Right
中返回loadFoo()
或loadBar()
返回None
,则预期结果为Either.Right(None)
我尝试了几件事。这个第一个示例可行,但是由于一堆嵌套的折叠以及RxJava和Either/Option
运算符的混合,因此所得代码难以阅读。
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) }
)
}
}
我尝试的第二件事是使用箭头的rxjava observable comprehensions。但是还不太清楚如何使它最终返回Single<Either<ApiError, Option>
。
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()
}
任何有关如何解决此问题或重组数据类型以使其更容易的帮助/建议将不胜感激!对于许多函数式编程概念来说还是很新的。
答案 0 :(得分:2)
如果我是您,我会考虑简化您的返回类型,而不要在Either
的上下文中使用Single
,因为Single
已经可以发出错误消息。因此,最后,您只能使用Either<ApiError, Option<Bar>>
来处理RxJava链中的错误,而不是在Option<Bar>
上进行平面映射。像这样:
class Foo(val qux: Option<Qux>)
class Bar
class Qux
class ApiError
fun loadFoo(): Single<Option<Foo>> {
// in case of an error, this will return Single.error(ApiError(...)) if ApiError extends Throwable
// otherwise make it extend it or just wrap it into something which is a Throwable
}
fun loadBar(qux: Qux): Single<Option<Bar>> {
// same as above
}
fun loadBarFromFooOption(maybeFoo: Option<Foo>): Single<Option<Bar>> {
return maybeFoo.flatMap { foo -> foo.qux }
.map { qux -> loadBar(qux) }
.getOrElse { Single.just(Option.empty()) }
}
fun doStuffToGetBar(): Single<Option<Bar>> {
return loadFoo().flatMap { fooOption -> loadBarFromFooOption(fooOption) }
}
// somewhere else
doStuffToGetBar().subscribe({ barOption -> /* ... */ }, { error -> /* ... */ })