Сложность использования arrow-kt Either, Option и RxJava вместе - PullRequest
0 голосов
/ 23 февраля 2019

У меня много трудностей, чтобы найти хороший способ координировать, используя RxJava вместе с типами 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>>> {
  ...
}

Цель состоит в том, чтобы возвратить результат loadBar(Qux) в RxJava 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 стрелки понимания .Но не мог понять, как заставить это возвращать 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 голосов
/ 23 февраля 2019

Если бы я был вами, я бы рассмотрел упрощение типов возвращаемых данных и не использовал бы Either в контексте 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 -> /* ... */ })
...