program below is crashing with exception
Exception in thread "main" 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 MainKt$fetch$1.invokeSuspend(main.kt:17)
at MainKt$fetch$1.invoke(main.kt)
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:127)
at arrow.typeclasses.Monad$DefaultImpls.fx(Monad.kt:81)
at arrow.effects.extensions.IOMonad$DefaultImpls.fx(io.kt)
at arrow.effects.extensions.io.monad.IOMonadKt$monad$1.fx(IOMonad.kt:195)
at arrow.effects.extensions.io.monad.IOMonadKt.fx(IOMonad.kt:178)
at MainKt.fetch(main.kt:16)
at MainKt$main$1$1.invoke(main.kt:10)
at MainKt$main$1$1.invoke(main.kt)
at arrow.effects.extensions.IOUnsafeRun$DefaultImpls.runNonBlocking(io.kt:192)
at arrow.effects.extensions.io.unsafeRun.IOUnsafeRunKt$unsafeRun$1.runNonBlocking(IOUnsafeRun.kt:38)
at arrow.effects.extensions.io.unsafeRun.IOUnsafeRunKt.runNonBlocking(IOUnsafeRun.kt:35)
at MainKt$main$1.invokeSuspend(main.kt:10)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
at kotlin.coroutines.ContinuationKt.startCoroutine(Continuation.kt:127)
at arrow.unsafe.invoke(unsafe.kt:31)
at MainKt.main(main.kt:9)
at MainKt.main(main.kt)
import arrow.core.Tuple2
import arrow.effects.IO
import arrow.effects.extensions.io.monad.fx
import arrow.effects.extensions.io.unsafeRun.runNonBlocking
import arrow.unsafe
import kotlinx.coroutines.delay
fun main() {
unsafe {
runNonBlocking(::fetch) {
it.fold({ it.printStackTrace() }, { println(it) })
}
}
}
fun fetch(): IO<Tuple2<List<Hero>, List<Comic>>> = fx {
val heroes = effect { fetchHeroes() }
val comics = effect { fetchComics() }
!tupled(heroes, comics)
}
suspend fun fetchHeroes(): List<Hero> {
delay(250)
return heroes
}
suspend fun fetchComics(): List<Comic> {
delay(250)
return comics
}
val heroes = listOf(Hero(id = 1, name = "Iron Man"), Hero(id = 2, name = "Thor"))
val comics = listOf(Comic(id = 1, name = "Comic 1"), Comic(id = 2, name = "Comic 2"))
data class Hero(
val id: Int,
val name: String
)
data class Comic(
val id: Int,
val name: String
)
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鈥檛 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 MonadSyntaxand 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.