Question about how to combine `Either<>`s th...
# arrow
s
Question about how to combine `Either<>`s that are wrapped in Rx `Single`s. This is the example, and it seems to work, but I wonder if there is an easier/more-concise/better way of doing this There are these two functions:
Copy code
fun getAccountKeys(): Single<Either<List<AccountKey>>>
and
Copy code
fun getAccount(key: AccountKey): Single<DataModelResult<Account>>
We’d like to create the following function, that will use the two above
Copy code
fun getAccounts(): Single<DataModelResult<List<Account>>>
(
DataModelResult<T>
=
Either<DataError, T>
) This is one implementation of `getAccounts()`:
Copy code
val result: Single<DataModelResult<List<Account>>> =
    getAccountKeys()
        .flatMap { listOfKeys: DataModelResult<List<AccountKey>> ->

            val rxOfKeys: DataModelResult<Observable<AccountKey>> =
                listOfKeys.map { Observable.fromIterable(it) }

            val rxOfAccounts: DataModelResult<Observable<DataModelResult<Account>>> =
                rxOfKeys.map { rxKey -> rxKey.flatMapSingle(::getAccount) }

            val rxOfListOfAccounts: DataModelResult<Single<List<DataModelResult<Account>>>> =
                rxOfAccounts.map { it.toList() }

            val single: Single<DataModelResult<List<Account>>> = when (rxOfListOfAccounts) {
                is Either.Left -> Single.just(rxOfListOfAccounts)
                is Either.Right -> rxOfListOfAccounts.b.map { listOfAccounts: List<DataModelResult<Account>> ->
                    listOfAccounts.fold(emptyList<Account>().just(), ::concat)
                }
            }

            single
        }
return result

.. and this top-level concat function ..
fun <L, T> concat(result: Either<L, List<T>>, item: Either<L, T>): Either<L, List<T>> =
    result.flatMap { list -> item.map { list + it } }
Is this the way to go or is there a more Arrow/Functional way to go (we use Arrow version
0.9.0
)? Thank you!
p
ye one sec
Copy code
getAccountKeys()
  .toObservable()
  .flatMap {
    it.fold({ Observable.empty() }, { Observable.fromIterable(it) })
  }.flatMap {
    getAccount(it).toObservable()
  }.toList()
  .toSingle()
ezpzlemonsqueezy
it’ll fail if Either is left or the List is empty tho
if you have
List<Either<E, List<A>>>
then
Copy code
list.map {
  it.fold({ Observable.empty() }, { Observable.fromIterable(it) })
}
and I believe there’s an operator to create an Observable from a list of Observables
so tl;dr flatten the list to Observable, call
getAccount
on each, collect to Single afterwards
use
concatMap
if you want to assure correct ordering
s
Thank you. But with your example, the result is a
Single<List<DataModelResult<Account>>>
, We’d need a
Single<DataModelResult<List<Account>>>
instead (swapped the List and the DataModelResult 🙂 )
Also, the final result no longer carries the
DataError
(if any was raised) from the
getAccountKeys()
call, since it was folded into a Observable.empty()….
p
what is a
DataModelResult
?
s
typealias DataModelResult<T> = Either<DataError, T>
p
so the question is: do you want to short-circuit on error or not
if any of the
Single<Either<List<AccountKey>>>
is
Left
, is the whole operation
Left
or not?
Copy code
getAccountKeys()
  .toObservable()
  .flatMap {
    it.fold({ 
      Observable.just(it.left()) 
    }, { 
      Observable.fromIterable(it) 
        .flatMap {
          getAccount(it).toObservable()
        }.toList()
        .map { it.right() }
    })
  }
  .toSingle()
that’s the only change you need
s
If one fails (getAccountKeys or any of the getAccount), the entire operation fails with the first encountered DataError
p
yes
check the latest
is that it?