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