Given two `Either<Left, Right>` , where `Lef...
# arrow
d
Given two
Either<Left, Right>
, where
Left
is
sealed interface Left { A; B }
I want
Right
if one of the two Either is Right; otherwise, the Either which
Left
is
A
, if any. In other words, the priority is
Right -> Left.A -> whatever
( don’t really need
Left.B
) Which would be the best way to achieve that?
Specifically, I have
Copy code
sealed interface GetAccountError { 
  data class NetworkError(...) : GetAccountError
  object NotConnected : GetAccountError
}

val tmdbEither: Either<GetAccountError, Account>
val traktEither: Either<GetAccountError, Account>
I want
Account
if any, otherwise,
AccountError
if any, otherwise I know no account is connected
So far, this is the cleanest way I found
Copy code
tmdbAccountUiModelEither.fold(
    ifLeft = { accountError ->
        if (accountError is GetAccountError.Network) {
            return@combine ManageAccountState.Account.Error(
                message = networkErrorMapper.toMessage(accountError.networkError)
            )
        }
    },
    ifRight = { account ->
        return@combine ManageAccountState.Account.Connected(account)
    }
)
traktAccountUiModelEither.fold(
    ifLeft = { accountError ->
        if (accountError is GetAccountError.Network) {
            return@combine ManageAccountState.Account.Error(
                message = networkErrorMapper.toMessage(accountError.networkError)
            )
        }
    },
    ifRight = { account ->
        return@combine ManageAccountState.Account.Connected(account)
    }
)
return@combine ManageAccountState.Account.NotConnected
s
I am not sure what
return@combine
is doing here 🤔 If I understand correctly, with
1.2.6-alpha.28
you can write:
Copy code
fun GetAccountError.Network.toManageError() =
  ManageAccountState.Account.Error(
                message = networkErrorMapper.toMessage(accountError.networkError)

tmdbEither.recover { e ->
  if(e is NetworkError) raise(e.toManageError())
  else traktEither.recover {
      if(e is NetworkError) raise(e.toManageError())
      else raise(ManageAccountState.Account.NotConnected)
    }
  }
}.map { ManageAccountState.Account.Connected(account) }
Or before:
Copy code
fun GetAccountError.Network.toManageError() =
  ManageAccountState.Account.Error(
                message = networkErrorMapper.toMessage(accountError.networkError)

tmdbEither.handleErrorWith { e ->
  if(e is NetworkError) e.toManageError().left()
  else traktEither.handleErrorWith {
      if(e is NetworkError) e.toManageError().left()
      else ManageAccountState.Account.NotConnected.left()
    }
  }
}.map { ManageAccountState.Account.Connected(account) }
d
Thanks, that's seems perfect! I'll try it tomorrow! P.s.
return@combine
just because I'm inside the lambda of a
combine
Copy code
combine(
  getTmbdAccount(),
  getTraktAcoount()
) {
  // Code above
}
s
Right, okay then I understood it correctly. I think the code above should work ☺️
d
Implemented your solution and did some refactoring (moved to domain). Looks so neat!
Copy code
operator fun invoke(): Flow<Either<GetAccountError, Account>> = combine(
    getTmdbAccount(),
    getTraktAccount()
) { tmdbAccountEither, traktAccountEither ->
    check(tmdbAccountEither.isLeft() || traktAccountEither.isLeft()) {
        "Both accounts are connected: this is not supported"
    }
    tmdbAccountEither.handleErrorWith { tmdbError ->
        traktAccountEither.handleErrorWith { traktError ->
            if (tmdbError is GetAccountError.Network) {
                tmdbError.left()
            } else {
                traktError.left()
            }
        }
    }
}