1

我很难找到一种使用 RxJava 以及箭头 ktEitherOption类型进行协调的好方法。我有两种方法都返回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>>> {
  ...
}

目标是将loadBar(Qux)RxJava中的结果Single作为 type返回Either<ApiError, Option<Bar>>

复杂性来自这样一个事实,即从返回的数据中检索到qux所需的参数(是类型的属性)。loadBar()SingleloadFoo()QuxFooOption<Qux>


期望的结果:

  • 发生的任何sApiError都会传递给SingleEither.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()
}

任何有关如何解决此问题或重组数据类型以使其更容易的帮助/建议将不胜感激!对于许多函数式编程概念来说仍然很新。

4

1 回答 1

2

如果我是你,我会考虑简化你的返回类型,而不是Either在 a 的上下文中使用Single,因为Single已经可以发出错误。所以最后,Either<ApiError, Option<Bar>>您只能使用Option<Bar>, 并处理 RxJava 链中的错误,而不是在 上进行平面映射。就像是:

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 -> /* ... */ })
于 2019-02-22T23:57:39.920 回答