https://kotlinlang.org logo
#arrow
Title
# arrow
t

Tower Guidev2

08/05/2022, 7:00 AM
Hi I need some guidance on how to employ
Either
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 type
s

simon.vergauwen

08/05/2022, 9:57 AM
Hey @Tower Guidev2, Can you share a complete example in a
text 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.
t

Tower Guidev2

08/05/2022, 11:14 AM
@simon.vergauwen thanks for taking the time to look at my issue its tricky to share my code what i am trying to achieve is to be able to call a sequence of functions and have them fail fast for example:-
Copy code
fun 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?
I have refactored my code to the following
Copy code
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"
s

simon.vergauwen

08/05/2022, 2:52 PM
what 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 calls
Okay, that is a very common use-case which is normally handled like this.
Copy code
either<Throwable, Result> {
  val x: Int = Either.catch {
      // throwing code returning Int
   }.tapLeft { throwable -> log(...) }
    .bind() // exits either if Left
   ...  
}
t

Tower Guidev2

08/05/2022, 2:58 PM
thanks can you achieve similar with
effect {}
? is one approach better than the other?
s

simon.vergauwen

08/05/2022, 3:02 PM
This uses
effect
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 🙂
t

Tower Guidev2

08/05/2022, 3:09 PM
Im using
Copy code
api platform('io.arrow-kt:arrow-stack:1.1.3-alpha.41')
api "io.arrow-kt:arrow-core"
api 'io.arrow-kt:arrow-core-retrofit'
i ended up with this code...
Copy code
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()
}
7 Views