streetsofboston
02/08/2019, 1:53 PMsuspend functions ‘functional’, possibly using Arrow-Fx. I threaded my question “How can I use suspend functions in a functional way” right here:streetsofboston
02/08/2019, 1:54 PMgetCity function of my ViewModel is a suspend function providing data to a LiveData instance that is subscribed to by a Fragment. The data-sources (locationDataSource and geoDataSource) have suspend funs as well.
The function getCity, getLocation and getReverse are all suspend returning an Either.
The first implementation below works, but only because flatMap is an
inline function. If it weren’t ‘inline’, this code would not compile, because getReverse is suspend.
private suspend fun getCity(): Either<MainViewModelError, StringResource> {
return locationDataSource.getLocation()
.flatMap { (lat, long) -> geoDataSource.getReverse(lat, long) }
.map { it.asResource }
.mapLeft { it.asMainViewModelError }
}streetsofboston
02/08/2019, 1:54 PMfx { ... effect { ... } .. }.
However, this code fails with a runtime exception:
private suspend fun getCity(): Either<MainViewModelError, StringResource> {
val result: Either<DataError, StringResource> = fx {
val (lat, long) = !!effect { locationDataSource.getLocation() }
val nameOfCity = !!effect { geoDataSource.getReverse(lat, long) }
nameOfCity.asResource
}
return result
.mapLeft { it.asMainViewModelError }
}streetsofboston
02/08/2019, 1:54 PMeffect is called, a stack-trace is produced:
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property retVal has not been initialized
at arrow.typeclasses.suspended.BlockingContinuation.getRetVal(MonadSyntax.kt:24)
at arrow.typeclasses.suspended.MonadSyntax$DefaultImpls.effect(MonadSyntax.kt:12)
at arrow.typeclasses.MonadContinuation.effect(MonadContinuations.kt:16)
at io.intrepid.mvvmfp.ui.main.MainViewModel$getCity$result$1$r$1.invokeSuspend(MainViewModel.kt:90)
at io.intrepid.mvvmfp.ui.main.MainViewModel$getCity$result$1$r$1.invoke(Unknown Source:10)
at arrow.typeclasses.Monad$fx$wrapReturn$1.invokeSuspend(Monad.kt:80)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
at kotlin.coroutines.ContinuationKt.startCoroutine(Continuation.kt:128)
at arrow.typeclasses.Monad$DefaultImpls.fx(Monad.kt:81)
at arrow.core.extensions.EitherMonad$DefaultImpls.fx(Unknown Source:8)
at arrow.core.extensions.either.monad.EitherMonadKt$monad$1.fx(EitherMonad.kt:194)
at arrow.typeclasses.MonadContinuation.fx(Unknown Source:7)
at io.intrepid.mvvmfp.ui.main.MainViewModel$getCity$result$1.invokeSuspend(MainViewModel.kt:89)
at io.intrepid.mvvmfp.ui.main.MainViewModel$getCity$result$1.invoke(Unknown Source:10)
at arrow.typeclasses.Monad$fx$wrapReturn$1.invokeSuspend(Monad.kt:80)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
at kotlin.coroutines.ContinuationKt.startCoroutine(Continuation.kt:128)
at arrow.typeclasses.Monad$DefaultImpls.fx(Monad.kt:81)
at arrow.core.extensions.EitherMonad$DefaultImpls.fx(Unknown Source:8)
at arrow.core.extensions.either.monad.EitherMonadKt$monad$1.fx(EitherMonad.kt:194)
at arrow.typeclasses.Monad$DefaultImpls.binding(Monad.kt:76)
at arrow.core.extensions.EitherMonad$DefaultImpls.binding(Unknown Source:8)
at arrow.core.extensions.either.monad.EitherMonadKt$monad$1.binding(EitherMonad.kt:194)
at arrow.core.extensions.either.monad.EitherMonadKt.binding(EitherMonad.kt:166)
at io.intrepid.mvvmfp.ui.main.MainViewModel.getCity(MainViewModel.kt:88)streetsofboston
02/08/2019, 1:55 PMsuspend functions in a functional way (if flatMap weren’t inline, i would have been stuck)?
Maybe I should not use fx ... inside a suspend function?
But how about the code in the original’s flatMap or, If I tried using just binding { ... } without fx { ... } instead; when calling other suspend funs when the lambda is non-suspend?
Is there a binding { ... } function variant (for Eithers) that accepts a suspend-lambda so that other suspend functions can be called?
Thank you!streetsofboston
02/08/2019, 7:25 PM0.8.2 and by wrapping calls inside DeferredK. But this adds a lot of extra code. Is there a simpler/flatter way than this code below?
private suspend fun getCity(): Either<MainViewModelError, StringResource> {
val result = binding {
val location = DeferredK(coroutineContext) { locationDataSource.getLocation() }.bind()
val nameOfCity = location.fold({ DeferredK { it.left() } }) { (lat, long) ->
DeferredK(coroutineContext) { geoDataSource.getReverse(lat, long) }
}.bind()
nameOfCity
}
return result.await()
.map { it.asResource }
.mapLeft { it.asMainViewModelError }
}pakoito
02/08/2019, 8:17 PMstreetsofboston
02/08/2019, 8:25 PMstreetsofboston
02/10/2019, 1:18 PMpakoito
02/10/2019, 1:20 PMpakoito
02/10/2019, 1:21 PMeffect block inside an fx blockpakoito
02/10/2019, 1:21 PMbind, !, or destructuringpakoito
02/10/2019, 1:22 PMstreetsofboston
02/10/2019, 1:25 PMpakoito
02/10/2019, 1:28 PMpakoito
02/10/2019, 1:28 PM--refresh-dependencies and use clean cache and restart in IJpakoito
02/10/2019, 1:29 PMstreetsofboston
02/10/2019, 1:34 PMstreetsofboston
02/10/2019, 1:38 PMIO<Either<Error, T>>, is their a way to have a binding (or fx) to be able to deal with T instead of Either<Error,T>, ie dig down two levels instead of one?pakoito
02/10/2019, 1:48 PM