ivanmorgillo
04/18/2021, 8:33 AMEither
when Result<T>
will be easily available in Kotlin 1.5 (https://github.com/Kotlin/KEEP/pull/244)
I'm planning some lessons for my students about Either
and I'm afraid they will question the benefits
once they will be able to use Result
. I'm not sure that Result
will have flatMap
& Co. tho 🤔
Any insight? 😄raulraja
04/18/2021, 9:15 AMEither
is generic in its error and left side while Result
has it’s left fixed to Throwable
.
What will happen in Arrow core is that once these restrictions are lifted and Result
becomes generically available you will see the APIs of Either
also available over Result
including support for Result
in either comprehensions and other places where its compatible. Ultimately Either is Either<E, A>
but Result is only Result<A>
where there is an implicit left Throwable embedded.raulraja
04/18/2021, 9:17 AMivanmorgillo
04/18/2021, 9:18 AMflatMap
on Result
?raulraja
04/18/2021, 9:19 AMresult.bind()
inside the either
and result
computation expressions.raulraja
04/18/2021, 9:20 AMraulraja
04/18/2021, 9:23 AMIterable
support + comprehensions or similar. If you or anyone want to give it a shot at porting the support we’d be happy to help with any questions and review. At some point when it’s stable will be added as we had discussed this before in our planning meetings.ivanmorgillo
04/18/2021, 9:39 AMstojan
04/18/2021, 9:47 AMivanmorgillo
04/18/2021, 9:57 AMEither
most of the time we, me and my students, only use it with Throwable
.
Sometime we have a sealed class
on the left, but then we hit the infamous:
Oh now we have 15 differentAt this point I should talk about Union Types. Coproduct... and we move fromtypes, one for every possible network call that can fail 😞NoInternet
This network call can fail with an error.to
WTF is this thing!? Can't we use try/catch and return?!null
ivanmorgillo
04/18/2021, 9:58 AMraulraja
04/18/2021, 9:59 AMsuspend () -> A
since the continuation exits always in Result<Throwable, A> and as long as you are in suspend you’ll be forced to handle at some point or blow up in suspend mainraulraja
04/18/2021, 9:59 AMivanmorgillo
04/18/2021, 10:00 AMsealed class {
NoInternet
UserNotFound
Timeout
}
raulraja
04/18/2021, 10:00 AMGenericError(Throwable)
in the ADTraulraja
04/18/2021, 10:01 AMraulraja
04/18/2021, 10:02 AMivanmorgillo
04/18/2021, 10:02 AMNoInternet
and Timeout
over and overraulraja
04/18/2021, 10:03 AMivanmorgillo
04/18/2021, 10:04 AMThrowable
because I don't want my ViewModel to know that IOException
is probably the user not having internet connectionivanmorgillo
04/18/2021, 10:04 AMraulraja
04/18/2021, 10:05 AMsealed class Error
object NoInternet : Error()
object UserNotFound: Error()
object Timeout: Error()
sealed class UiError
data class ServiceError(val error: Error) : UiError()
object ScreenOff : UiError()
ivanmorgillo
04/18/2021, 10:05 AMsuspend fun loadUser(id): Either<Coproduct<NoInternet, TimeOut, NoUser>>, User>
raulraja
04/18/2021, 10:05 AMError
may be in a different moduleraulraja
04/18/2021, 10:06 AMraulraja
04/18/2021, 10:06 AMivanmorgillo
04/18/2021, 10:07 AMError
in a screen that doesn't need UserNotFound
, when I do when(error)
I get also a case that I don't need, right?raulraja
04/18/2021, 10:10 AMServiceError
or you cuold also have the Either<TypeICare, *> in that screen only. This is how decision in typing forces you to go from an error type to another across layers.raulraja
04/18/2021, 10:11 AMraulraja
04/18/2021, 10:12 AMivanmorgillo
04/18/2021, 10:38 AMsuspend fun loadUser(id) : Either<XYZ, User>
suspend fun loadFriends(id) : Either<ABC, Nel<Friend>>
Both can fail with
NoInternet
SlowInternet
On top of that, the first one can fail also with UserNotFound
, while the second one can fail with NoFriendsFound
.
How could I model it to be sure that UserProfileViewModel
is able to work with
NoInternet
SlowInternet
UserNotFound
and FriendsListViewModel
can work with
kotlin
NoInternet
SlowInternet
NoFriendsFound
This is my pain point right now.ivanmorgillo
04/18/2021, 10:39 AMstojan
04/18/2021, 11:38 AM// suspend fun loadUser(id) : Either<XYZ, User>
sealed class UserError {
object Timeout : UserError()
object NoInternet : UserError()
object UserNotFound : UserError()
}
//suspend fun loadFriends(id) : Either<ABC, Nel<Friend>>
sealed class FriendsError {
object Timeout : FriendsError()
object NoInternet : FriendsError()
object NoFriendsFound : FriendsError()
}
// We can extract duplicaton
sealed class NetworkError<A> {
object Timeout : NetworkError<Nothing>()
object NoInternet : NetworkError<Nothing>()
data class ApiError<A>(val a: A) : NetworkError<A>()
}
// Now we can use composition
object UserNotFound
typealias UserError2 = NetworkError<UserNotFound>
object NoFriendsFound
typealias FriendsError2 = NetworkError<NoFriendsFound>
// However, are we handling Timeout and NoInternet differently?
// Probably not, at most we have a different error message + retry button
// Usually we have a generic: We couldn't connect, check your network and try again
// Do we really handle UserNotFound and NoFriendsFound differently?
// From domain perspective we asked for a resource and it's not there
// We can actually handle the different error message in the VM
// In this scenario a retry would probably not help, the resource won't magically appear
sealed class ApiError {
object InternetError : ApiError() // Timeout + NoInternet
object NotFound : ApiError() // UserNotFound + FriendsNotFound
}
simon.vergauwen
04/19/2021, 7:13 AMsealed interface
in Kotlin 1.5 and later will also really help. Then you can mix in multiple sealed interfaces
into a singe ADT.