Another question that may you guys could help me. ...
# arrow
j
Another question that may you guys could help me. Iโ€™m working on a functionality that fetch a list of users and validate each to know if it need to be deleted or not. I have 2 methods. first one is to fetch users
fun getUsers(max: Int): Either<LowUnprotectedError, List<User>>
and the other one is the validate method
fun isCandidateToDelete(user: User): Either<LowUnprotectedError, Boolean>
. Then i need to star the process for each user, but given the method to validate the user is in a Either, I need to fold it in order to know the result of verification like this :
Copy code
val usersDelete = users.filter { repository.isCandidateToDelete(it).fold({ false }, { it }) }`
Could you imagine a better way to accomplish this process. Please let me know if i was not clear with this question, thanks in advance!!
s
Hey @Jimmy Alvarez, The
either
computation block should work perfectly for you here. You can use
either { }
for
suspend
code or
either.eager
for non-suspending code. And you can rewrite your code to the following snippet:
Copy code
import arrow.core.computations.either

suspend fun program(): Either<LowUnprotectedError, List<User>> =
  either {
    val users = getUsers(100).bind()
    users.filter { isCandidate(it).bind() }
  }
Where
bind()
can safely extract the value from
Either.Right
and when
Either.Left
is encountered then it just exits the
either { }
block with the
Left
value. This allows for writing code with
Either
in an imperative way ๐Ÿ™‚
j
Yes, but in my case that is a problem, i would like to evaluate all the users in the list even if one of them fails for some reason. I do not want it to be short circuit process on error cases if any error would be better log in which is a side effect that i do not know how to represent.
Copy code
return either.eager<LowUnprotectedError, List<ProcessResult>> {
            val users = repository.getUsers(350000).bind()
            val usersToKillSession = users.filter { repository.isCandidateToKillSession(it).fold({ false }, { it }) }
            usersToKillSession.map { user -> publishUsersResult(user) } + enrolledUsers.map { user -> deleteUsersResult(user) }
        }.fold(
                {
                    LOGGER.error("Error processing users. Cause: ${it.cause}")
                    ResponseEntity<Response>(HttpStatus.OK)
                },
                {
                    it.forEach { <http://LOGGER.info|LOGGER.info>("User: ${it.user}. result: ${it.msj}. error: ${it.cause}") }
                    ResponseEntity<Response>(HttpStatus.OK)
                }
        )
I know this is not the ideal but it is what i have ehhehe ๐Ÿ˜„ ๐Ÿ˜„
s
Oh, okay. That's a bit of a special case but there is a solution for it ๐Ÿ˜‰ Let me introduce some types, and lets see if I understood correctly ๐Ÿ˜„
Copy code
val users: List<User> = repository.getUsers(350000).bind()
val errorOrResult = users.map { isCanidateToKillSession(it) }
val (errors, results) = errorsOrResults.separateEither()
Where
seperateEither
splits
Iterable<Either<A, B>>
into
Pair<List<A>, List<B>>
so you can inspect all left sides and all right sides in the list. Additionally, since you are doing
map
here on
Iterable
instead of traverse all your elements will be processed without short-circuiting.
j
mind blown
ajjajajajja that is the exact thing i needed!!!!!
You knew you are the boss?!!! thanks a lot!!!
๐Ÿ˜‚ 1
Just one thing, given we are evaluating the Either from the
isCanidateToKillSession
we lose
user
object so by the time you need to log which users were killed basically you could not access it. you loosed that context.
At the end, you got a List of Booleans
s
Yes, in this case you'll probably not want to return
Either<LowProtectedError, Boolean>
from
isCandidateToKillSession
but
Either<LowProtectedError, User>
or
Either<LowProtectedError, ProcessedUser>
๐Ÿ™Œ 1
arrow 1
j
Perfect! thanks
s
Anytime @Jimmy Alvarez ๐Ÿ‘