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

Fix #2764 & #2760 #2765

Merged
merged 7 commits into from Jul 16, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -568,7 +568,7 @@ import kotlin.coroutines.resume
* ```
* <!--- KNIT example-effect-guide-13.kt -->
*/
public interface Effect<out R, out A> {
public sealed interface Effect<out R, out A> {
/**
* Runs the suspending computation by creating a [Continuation], and running the `fold` function
* over the computation.
Expand Down Expand Up @@ -704,6 +704,10 @@ internal class Token {
* the result.
*/
@PublishedApi
@Deprecated(
"This will become private in Arrow 2.0, and is not going to be visible from binary anymore",
level = DeprecationLevel.WARNING
)
internal class FoldContinuation<B>(
private val token: Token,
override val context: CoroutineContext,
Expand Down Expand Up @@ -754,7 +758,11 @@ internal class FoldContinuation<B>(
*/
public fun <R, A> effect(f: suspend EffectScope<R>.() -> A): Effect<R, A> = DefaultEffect(f)

private class DefaultEffect<R, A>(private val f: suspend EffectScope<R>.() -> A) : Effect<R, A> {
@Deprecated(
"This will be removed in Arrow 2.0",
level = DeprecationLevel.WARNING
)
internal class DefaultEffect<R, A>(val f: suspend EffectScope<R>.() -> A) : Effect<R, A> {
// We create a `Token` for fold Continuation, so we can properly differentiate between nested
// folds
override suspend fun <B> fold(recover: suspend (R) -> B, transform: suspend (A) -> B): B =
Expand Down
Expand Up @@ -54,7 +54,10 @@ public interface EffectScope<in R> {
* ```
* <!--- KNIT example-effect-scope-02.kt -->
*/
public suspend fun <B> Effect<R, B>.bind(): B = fold(this@EffectScope::shift, ::identity)
public suspend fun <B> Effect<R, B>.bind(): B =
when (this) {
is DefaultEffect -> f(this@EffectScope)
}

/**
* Runs the [EagerEffect] to finish, returning [B] or [shift] in case of [R],
Expand Down
Expand Up @@ -21,6 +21,7 @@ import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
import kotlin.coroutines.startCoroutine
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class EffectSpec :
StringSpec({
Expand Down Expand Up @@ -241,8 +242,27 @@ class EffectSpec :
}.runCont()
} shouldBe Either.Left(e)
}

"Dispatching in nested Effect causes incorrect behavior" {
serras marked this conversation as resolved.
Show resolved Hide resolved
checkAll(Arb.string()) { msg ->
fun failure(): Effect<Failure, String> = effect {
withContext(Dispatchers.Default) {}
shift(Failure(msg))
}

effect<Failure, Int> {
failure().bind()
1
}.fold(
recover = { it },
transform = { fail("Should never come here") },
) shouldBe Failure(msg)
}
}
})

private data class Failure(val msg: String)

suspend fun currentContext(): CoroutineContext = kotlin.coroutines.coroutineContext

internal suspend fun Throwable.suspend(): Nothing = suspendCoroutineUninterceptedOrReturn { cont ->
Expand Down