Closed marenovakovic closed 5 years ago
wrong fx
import use import arrow.effects.extensions.io.fx.fx
instead of import arrow.effects.extensions.io.monad.fx
Let's keep it open because it shouldn't crash in any case. cc @raulraja
I think it was confirmed in gitter that in the new snapshot the issue was gone. Is that not the case?
it is not the same error
Snapshot versions are cached and don’t refresh unless you use the gradle flag --refresh-dependencies
and click on clean cache and restart
in IntelliJ
I encountered the same issue: https://kotlinlang.slack.com/archives/C5UPMM0A0/p1549634015443400?thread_ts=1549634015.443400&cid=C5UPMM0A0
This was on Feb 8. I haven't retried it with the latest version of the snapshot. I can do that later today or tomorrow.
At this moment, with the latest 0.9.0-SNAPSNOT downloaded, the issue is still there. I'll repost the code and stack trace below, where getLocation
and getReverse
are suspend
functions as well:
Code:
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 }
}
Stack trace caused when calling this function:
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.invokeSuspend(MainViewModel.kt:78)
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.suspended.monad.Fx$DefaultImpls.fx(Fx.kt:10)
at arrow.core.extensions.EitherFx$DefaultImpls.fx(Unknown Source:8)
at arrow.core.extensions.either.fx.EitherFxKt$fx$1.fx(EitherFx.kt:35)
at arrow.core.extensions.either.fx.EitherFxKt.fx(EitherFx.kt:22)
at io.intrepid.mvvmfp.ui.main.MainViewModel.getCity(MainViewModel.kt:77)
...
Could it be related to https://github.com/arrow-kt/arrow/issues/1277 ?
Another small reproduction code sample:
suspend fun getValue1() : Either<Throwable, Int> {
delay(100) // <-- This make the kotlin.UninitializedPropertyAccessException happen
return 5.just()
}
suspend fun getValue2() : Either<Throwable, Int> {
return 10.just()
}
fun main() {
val result = fx<Throwable, Int> {
val v1 = effect { getValue1() }
val v2 = effect { getValue2() }
!!v1 + !!v2
}
println(result)
}
If you change delay(100)
to delay(1)
, the crash sometimes happens. Often "Right(b=15)" is printed, though....
If you remove the delay(100)
entirely, the crash does not happen and "Right(b=15)" is printed.
I've ran into a similar problem when working with coroutines and this post helped a lot. Maybe it can help to fix this issue.
Can you please try to do the same with fx fixed to IO or Observable instead of Either?
Ok, I see what is going on here. This is using the master snapshot which contains effect
in MonadSyntax
and a BlockingContinuation. This is is why you can use effect
on either and that is incorrect. Will submit a fix before 0.9.0 and refer to this issue. effect
can't be used on Either and won't be available simply because Either is eager and can't suspend side effects.
Additionally the only way to implement this on either is to actually block the main thread on that delay
suspension which is something we are not gonna do.
The following program shows how it works if you use the IO fx which is a suspend capable monad.
package arrow.effects.zio
import arrow.core.Either
import arrow.core.extensions.either.applicative.just
import arrow.core.extensions.either.applicative.map
import arrow.effects.IO
import arrow.effects.extensions.io.fx.fx
import arrow.effects.extensions.io.unsafeRun.runBlocking
import arrow.unsafe
import kotlinx.coroutines.delay
suspend fun getValue1(): Either<Throwable, Int> {
delay(100) // <-- Either can't suspend anything because it's an eager data type, so you can't use the `either fx`
return 5.just()
}
suspend fun getValue2(): Either<Throwable, Int> {
return 10.just()
}
val program: IO<Either<Throwable, Int>> =
fx {
val v1 = !effect { getValue1() }
val v2 = !effect { getValue2() }
val result = map(v1, v2) { it.a + it.b }
!effect { println(result) }
result
}
fun main() {
unsafe { runBlocking { program } } //Right(b=15)
}
To conclude:
effect
won't be available in data types that can't suspend side effectsConcurrent
for EitherT
, Kleisli
and in general all transformers data types that delegate to the inner monad. You could have used the EitherT
fx
here if we had those instances but currently it's fx
only reaches MonadError
which is also unable to suspend effects.Will keep this open to reflect those change but if anyone wants to take a shot at it you just need to remove effect
from MonadSyntax
and get rid of BlockingContinuation
. Would love the help as most of the current maintainer team are currently focused on bigger tasks also related to Fx.
/cc @javipacheco I don't need you to check this on Android anymore, thanks anyway 😗
working on this now.
program below is crashing with exception