难以同时使用arrow-kt,Option和RxJava

时间:2019-02-22 23:11:22

标签: kotlin functional-programming reactive-programming rx-java2 arrow-kt

我很难找到一种很好的方法来与RkJava以及arrow-kt EitherOption类型一起使用。我有两种方法都返回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()发出的数据中检索的(QuxFoo的属性,类型为Option<Qux>)。


所需结果:

  • 发生的任何ApiError都将传递给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()
}

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

1 个答案:

答案 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 -> /* ... */ })