Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fx crash #1295

Closed
marenovakovic opened this Issue Feb 9, 2019 · 14 comments

Comments

Projects
None yet
5 participants
@marenovakovic
Copy link

marenovakovic commented Feb 9, 2019

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
)
@marenovakovic

This comment has been minimized.

Copy link
Author

marenovakovic commented Feb 9, 2019

wrong fx import use import arrow.effects.extensions.io.fx.fx instead of import arrow.effects.extensions.io.monad.fx

@pakoito pakoito reopened this Feb 9, 2019

@pakoito

This comment has been minimized.

Copy link
Member

pakoito commented Feb 9, 2019

Let's keep it open because it shouldn't crash in any case. cc @raulraja

@raulraja

This comment has been minimized.

Copy link
Member

raulraja commented Feb 9, 2019

I think it was confirmed in gitter that in the new snapshot the issue was gone. Is that not the case?

@marenovakovic

This comment has been minimized.

Copy link
Author

marenovakovic commented Feb 9, 2019

it is not the same error

@pakoito

This comment has been minimized.

Copy link
Member

pakoito commented Feb 10, 2019

WARNING!

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

@streetsofboston

This comment has been minimized.

Copy link

streetsofboston commented Feb 10, 2019

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.

@streetsofboston

This comment has been minimized.

Copy link

streetsofboston commented Feb 11, 2019

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)
        ...
@streetsofboston

This comment has been minimized.

Copy link

streetsofboston commented Feb 11, 2019

Could it be related to #1277 ?

@streetsofboston

This comment has been minimized.

Copy link

streetsofboston commented Feb 26, 2019

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.

@alissonfpmorais

This comment has been minimized.

Copy link
Contributor

alissonfpmorais commented Feb 26, 2019

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.

@pakoito

This comment has been minimized.

Copy link
Member

pakoito commented Feb 26, 2019

Can you please try to do the same with fx fixed to IO or Observable instead of Either?

@raulraja

This comment has been minimized.

Copy link
Member

raulraja commented Feb 26, 2019

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 effects
  • We need extensions for Concurrent 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.
  • BlockingContinuation goes away

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.

@raulraja

This comment has been minimized.

Copy link
Member

raulraja commented Feb 26, 2019

/cc @javipacheco I don't need you to check this on Android anymore, thanks anyway 😗

@raulraja

This comment has been minimized.

Copy link
Member

raulraja commented Feb 27, 2019

working on this now.

raulraja added a commit that referenced this issue Feb 27, 2019

- Fixes #1295 and #1277 by eliminating effect capability from data ty…
…pes that are unable to suspend effects

- Reencodes Ank in terms of `fx`

raulraja added a commit that referenced this issue Feb 27, 2019

- Fixes #1295 and #1277 by eliminating effect capability from data ty…
…pes that are unable to suspend effects (#1319)

- Reencodes Ank in terms of `fx`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.