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.