Tower Guidev2
08/05/2022, 7:00 AMEither
while completing related operations
for example i am calling retrofit
api calls then persisting the data into my local sqlite
database
i am employing io.arrow-kt:arrow-core-retrofit
for my retrofit
calls as follows:-
/**
* <https://api.discogs.com/artists/45467>
*
*/
@GET("artists/{artist_id}")
suspend fun artist(@Header(HEADER_AUTHORISATION) accessToken: String = ACCESS_TOKEN, @Path("artist_id") artistId: Long): Either<CallError, ArtistResponse>
then in my worker:-
override suspend fun doActualWork(): Result {
return repository
.artist(artistId = 45467)
.fold(
ifLeft = { callError -> consumeCallError(callError) },
ifRight = {
val artist = ARTIST_MAPPER.map(it)
println(artist)
repository.database.artistDao().insertAsync(artist)
Result.success()
})
}
how can "wrap" or employ Either for the code i execute in my ifRight block above?
e.g. the database insert. As I am using Room and it does not accept Either typesimon.vergauwen
08/05/2022, 9:57 AMtext snippet
? It’ shard to follow the code here since I am not sure about the other signatures.
There are some good docs on Either, and Effect here.
https://arrow-kt.io/docs/apidocs/arrow-core/arrow.core/-either/
https://arrow-kt.io/docs/apidocs/arrow-core/arrow.core.continuations/-effect/
Hope that helps. If not I’d be happy to help you if you could provide a more complete example. I’m not sure I understood the problem correctly.Tower Guidev2
08/05/2022, 11:14 AMfun manageMultipleCalls() {
val stage1: Either<Bad1, Good1> = service.call()
val stage2: Either<Bad2, Good2> = database.call(stage1.good1)
val stage3: Either<Bad3, Good3> = somethingElse.call(stage2.good2)
}
when any stage throws an exception i need to log the exception details and stop processing further calls
do i have to add if/when statements after each stage to check isLeft/isRight?
is there a better approach?override suspend fun doActualWork(): Result = repository
.artist(artistId = 45467L)
.fold(
ifLeft = { callError -> consumeCallError(callError) },
ifRight = { artistResponse ->
when (val result = Either.catch { repository.database.artistDao().insertAsync(ARTIST_MAPPER.map(artistResponse)) }) {
is Either.Left -> consumeDatabaseError(result.value)
is Either.Right -> Result.success()
}
}
)
which is fine in this case as i only have two calls to contend with, e.g. the repository.artist
retrofit call and the database repository.database.artistDao().insertAsync()
call
however if i had three calls to process in sequence its going to start to get "messy"simon.vergauwen
08/05/2022, 2:52 PMwhat i am trying to achieve is to be able to call a sequence of functions and have them fail fast
when any stage throws an exception i need to log the exception details and stop processing further callsOkay, that is a very common use-case which is normally handled like this.
either<Throwable, Result> {
val x: Int = Either.catch {
// throwing code returning Int
}.tapLeft { throwable -> log(...) }
.bind() // exits either if Left
...
}
Tower Guidev2
08/05/2022, 2:58 PMeffect {}
?
is one approach better than the other?simon.vergauwen
08/05/2022, 3:02 PMeffect
under the hood if you’re using latest Arrow version since either { }
is effect { }.toEither()
.
Where toEither()
is fold({ Either.Left(it) }, { Either.Right(it) })
.
So one approach is not better as the other, it depends on your use-case 🙂Tower Guidev2
08/05/2022, 3:09 PMapi platform('io.arrow-kt:arrow-stack:1.1.3-alpha.41')
api "io.arrow-kt:arrow-core"
api 'io.arrow-kt:arrow-core-retrofit'
override suspend fun doActualWork(): Result = when(actualWork(45467L)) {
is Either.Left -> Result.failure()
is Either.Right -> Result.success()
}
private suspend fun actualWork(artistId: Long) = either {
val artistResponse = repository.artist(artistId = artistId).tapLeft { networkError -> consumeNetworkError(networkError) }.bind()
Either.catch {
repository.database.artistDao().insertAsync(ARTIST_MAPPER.map(artistResponse))
}.tapLeft { databaseError -> consumeDatabaseError(databaseError) }.bind()
}