From 5ba9f7ff3bbd8d600899c1c8aed291a4aa7e6f42 Mon Sep 17 00:00:00 2001 From: James Yox Date: Tue, 9 May 2023 18:51:55 -0600 Subject: [PATCH 1/9] WIP: Attempt at fixing ior recover issue --- .../commonMain/kotlin/arrow/core/raise/Builders.kt | 2 +- .../src/commonMain/kotlin/arrow/core/raise/Raise.kt | 9 +++++++++ .../commonTest/kotlin/arrow/core/raise/IorSpec.kt | 13 +++++++++---- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt index b9ee6d87496..3c9b22d7d2b 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt @@ -156,7 +156,7 @@ public class IorRaise @PublishedApi internal constructor( map { it.bind() } @RaiseDSL - public fun Ior.bind(): A = + public override fun Ior.bind(): A = when (this) { is Ior.Left -> raise(value) is Ior.Right -> value diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Raise.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Raise.kt index ca32711adcb..6f12b515fae 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Raise.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Raise.kt @@ -6,6 +6,7 @@ package arrow.core.raise import arrow.core.Either +import arrow.core.Ior import arrow.core.NonEmptyList import arrow.core.NonEmptySet import arrow.core.Validated @@ -238,6 +239,14 @@ public interface Raise { is Either.Right -> value } + @RaiseDSL + public fun Ior.bind(): A = + when (this) { + is Ior.Left -> raise(value) + is Ior.Right -> value + is Ior.Both -> rightValue + } + public fun Map>.bindAll(): Map = mapValues { (_, a) -> a.bind() } diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/IorSpec.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/IorSpec.kt index e8862e18845..dacad813850 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/IorSpec.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/IorSpec.kt @@ -3,15 +3,11 @@ package arrow.core.raise import arrow.core.Either import arrow.core.Ior import arrow.core.test.nonEmptyList -import arrow.typeclasses.Semigroup import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import io.kotest.property.Arb -import io.kotest.property.arbitrary.filter import io.kotest.property.arbitrary.int -import io.kotest.property.arbitrary.list -import io.kotest.property.arbitrary.string import io.kotest.property.checkAll import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll @@ -79,4 +75,13 @@ class IorSpec : StringSpec({ } }.message shouldBe "Boom!" } + + "Recover works as expected" { + ior(String::plus) { + val one = recover({ Ior.Left("Hello").bind() }) { 1 } + val two = Ior.Right(2).bind() + val three = Ior.Both(", World", 3).bind() + one + two + 3 + } shouldBe Ior.Both(", World", 6) + } }) From 5e26c37c88a17a81250d0860ed90d9b6765a0f21 Mon Sep 17 00:00:00 2001 From: James Yox Date: Sat, 27 May 2023 19:41:49 -0600 Subject: [PATCH 2/9] Override recover vs bind to fix recover behavior and add tests to all raise APIs --- .../kotlin/arrow/core/raise/Builders.kt | 49 +++++++++++++++++-- .../kotlin/arrow/core/raise/Raise.kt | 8 --- .../kotlin/arrow/core/raise/NullableSpec.kt | 8 +++ .../kotlin/arrow/core/raise/OptionSpec.kt | 9 ++++ .../kotlin/arrow/core/raise/ResultSpec.kt | 8 +++ 5 files changed, 71 insertions(+), 11 deletions(-) diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt index 3c9b22d7d2b..4b25d189c6a 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt @@ -7,6 +7,7 @@ package arrow.core.raise import arrow.atomic.Atomic import arrow.atomic.updateAndGet import arrow.core.Either +import arrow.core.EmptyValue.combine import arrow.core.Ior import arrow.core.NonEmptyList import arrow.core.NonEmptySet @@ -72,6 +73,15 @@ public class NullableRaise(private val raise: Raise) : Raise by rais contract { returns() implies (value != null) } return ensureNotNull(value) { null } } + + @RaiseDSL + public inline fun recover( + @BuilderInference block: NullableRaise.() -> A, + @BuilderInference recover: () -> A, + ): A = when (val nullable = nullable(block)) { + null -> recover() + else -> nullable + } } public class ResultRaise(private val raise: Raise) : Raise by raise { @@ -96,6 +106,15 @@ public class ResultRaise(private val raise: Raise) : Raise @JvmName("bindAllResult") public fun NonEmptySet>.bindAll(): NonEmptySet = map { it.bind() } + + @RaiseDSL + public inline fun recover( + @BuilderInference block: ResultRaise.() -> A, + @BuilderInference recover: (Throwable) -> A, + ): A = result(block).fold( + onSuccess = { it }, + onFailure = { recover(it) } + ) } public class OptionRaise(private val raise: Raise) : Raise by raise { @@ -129,10 +148,19 @@ public class OptionRaise(private val raise: Raise) : Raise by raise contract { returns() implies (value != null) } return ensureNotNull(value) { None } } + + @RaiseDSL + public inline fun recover( + @BuilderInference block: OptionRaise.() -> A, + @BuilderInference recover: () -> A, + ): A = when (val option = option(block)) { + is None -> recover() + is Some -> option.value + } } public class IorRaise @PublishedApi internal constructor( - private val combineError: (Error, Error) -> Error, + @PublishedApi internal val combineError: (Error, Error) -> Error, private val state: Atomic>, private val raise: Raise, ) : Raise { @@ -156,7 +184,7 @@ public class IorRaise @PublishedApi internal constructor( map { it.bind() } @RaiseDSL - public override fun Ior.bind(): A = + public fun Ior.bind(): A = when (this) { is Ior.Left -> raise(value) is Ior.Right -> value @@ -170,8 +198,23 @@ public class IorRaise @PublishedApi internal constructor( public fun Map>.bindAll(): Map = mapValues { (_, v) -> v.bind() } - private fun combine(other: Error): Error = + @PublishedApi + internal fun combine(other: Error): Error = state.updateAndGet { prev -> Some(prev.map { combineError(it, other) }.getOrElse { other }) }.getOrElse { other } + + @RaiseDSL + public inline fun recover( + @BuilderInference block: IorRaise.() -> A, + @BuilderInference recover: (error: Error) -> A, + ): A = when (val ior = ior(combineError, block)) { + is Ior.Both -> { + combine(ior.leftValue) + ior.rightValue + } + + is Ior.Left -> recover(ior.value) + is Ior.Right -> ior.value + } } diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Raise.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Raise.kt index 6f12b515fae..74d876e4c2a 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Raise.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Raise.kt @@ -239,14 +239,6 @@ public interface Raise { is Either.Right -> value } - @RaiseDSL - public fun Ior.bind(): A = - when (this) { - is Ior.Left -> raise(value) - is Ior.Right -> value - is Ior.Both -> rightValue - } - public fun Map>.bindAll(): Map = mapValues { (_, a) -> a.bind() } diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/NullableSpec.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/NullableSpec.kt index 20ccbb64a53..90054ec9b62 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/NullableSpec.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/NullableSpec.kt @@ -118,4 +118,12 @@ class NullableSpec : StringSpec({ either.bind() + 3 } shouldBe 7 } + + "Recover works as expected" { + nullable { + val one: Int = recover({ null.bind() }) { 1 } + val two = 2.bind() + one + two + } shouldBe 3 + } }) diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/OptionSpec.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/OptionSpec.kt index a645c3b468d..7490e643be3 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/OptionSpec.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/OptionSpec.kt @@ -1,6 +1,7 @@ package arrow.core.raise import arrow.core.None +import arrow.core.Some import arrow.core.some import arrow.core.toOption import io.kotest.core.spec.style.StringSpec @@ -39,4 +40,12 @@ class OptionSpec : StringSpec({ throw IllegalStateException("This should not be executed") } shouldBe None } + + "Recover works as expected" { + option { + val one: Int = recover({ None.bind() }) { 1 } + val two = Some(2).bind() + one + two + } shouldBe Some(3) + } }) diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/ResultSpec.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/ResultSpec.kt index e4123903edc..db631c2700b 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/ResultSpec.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/ResultSpec.kt @@ -20,4 +20,12 @@ class ResultSpec : StringSpec({ "Result - raise" { result { raise(boom) } shouldBe Result.failure(boom) } + + "Recover works as expected" { + result { + val one: Int = recover({ Result.failure(boom).bind() }) { 1 } + val two = Result.success(2).bind() + one + two + } shouldBe Result.success(3) + } }) From 8545448a4bd544ef2b7725284968ac2063c9f54f Mon Sep 17 00:00:00 2001 From: franciscodr Date: Mon, 29 May 2023 22:58:38 +0200 Subject: [PATCH 3/9] Update API files --- arrow-libs/core/arrow-core/api/arrow-core.api | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arrow-libs/core/arrow-core/api/arrow-core.api b/arrow-libs/core/arrow-core/api/arrow-core.api index c9495593591..cb975ee3851 100644 --- a/arrow-libs/core/arrow-core/api/arrow-core.api +++ b/arrow-libs/core/arrow-core/api/arrow-core.api @@ -3283,9 +3283,12 @@ public final class arrow/core/raise/IorRaise : arrow/core/raise/Raise { public final fun bindAllIor (Ljava/util/Map;)Ljava/util/Map; public final fun bindAllIor (Ljava/util/Set;)Ljava/util/Set; public fun catch (Larrow/core/continuations/Effect;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun combine (Ljava/lang/Object;)Ljava/lang/Object; + public final fun getCombineError ()Lkotlin/jvm/functions/Function2; public fun invoke (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public fun invoke (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun raise (Ljava/lang/Object;)Ljava/lang/Void; + public final fun recover (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public fun shift (Ljava/lang/Object;)Ljava/lang/Object; } @@ -3313,6 +3316,7 @@ public final class arrow/core/raise/NullableRaise : arrow/core/raise/Raise { public fun invoke (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public synthetic fun raise (Ljava/lang/Object;)Ljava/lang/Void; public fun raise (Ljava/lang/Void;)Ljava/lang/Void; + public final fun recover (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object; public synthetic fun shift (Ljava/lang/Object;)Ljava/lang/Object; public fun shift (Ljava/lang/Void;)Ljava/lang/Object; } @@ -3342,6 +3346,7 @@ public final class arrow/core/raise/OptionRaise : arrow/core/raise/Raise { public fun invoke (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun raise (Larrow/core/None;)Ljava/lang/Void; public synthetic fun raise (Ljava/lang/Object;)Ljava/lang/Void; + public final fun recover (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object; public fun shift (Larrow/core/None;)Ljava/lang/Object; public synthetic fun shift (Ljava/lang/Object;)Ljava/lang/Object; } @@ -3506,6 +3511,7 @@ public final class arrow/core/raise/ResultRaise : arrow/core/raise/Raise { public fun invoke (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public synthetic fun raise (Ljava/lang/Object;)Ljava/lang/Void; public fun raise (Ljava/lang/Throwable;)Ljava/lang/Void; + public final fun recover (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public synthetic fun shift (Ljava/lang/Object;)Ljava/lang/Object; public fun shift (Ljava/lang/Throwable;)Ljava/lang/Object; } From d77fae7eedcd8735a5f61c74fbc1ab5e712981e3 Mon Sep 17 00:00:00 2001 From: James Yox Date: Mon, 29 May 2023 19:32:14 -0600 Subject: [PATCH 4/9] Update arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/IorSpec.kt Co-authored-by: Francisco Diaz --- .../src/commonTest/kotlin/arrow/core/raise/IorSpec.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/IorSpec.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/IorSpec.kt index dacad813850..007135ee848 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/IorSpec.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/IorSpec.kt @@ -81,7 +81,7 @@ class IorSpec : StringSpec({ val one = recover({ Ior.Left("Hello").bind() }) { 1 } val two = Ior.Right(2).bind() val three = Ior.Both(", World", 3).bind() - one + two + 3 + one + two + three } shouldBe Ior.Both(", World", 6) } }) From bdda47194593abfde96228fb634994a2a96d9c24 Mon Sep 17 00:00:00 2001 From: James Yox Date: Mon, 29 May 2023 19:33:53 -0600 Subject: [PATCH 5/9] Removed Unnecessary Import --- .../arrow-core/src/commonMain/kotlin/arrow/core/raise/Raise.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Raise.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Raise.kt index 74d876e4c2a..ca32711adcb 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Raise.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Raise.kt @@ -6,7 +6,6 @@ package arrow.core.raise import arrow.core.Either -import arrow.core.Ior import arrow.core.NonEmptyList import arrow.core.NonEmptySet import arrow.core.Validated From 0ac3dba53f1cb54f45c115cd099653a31bdb4ac0 Mon Sep 17 00:00:00 2001 From: James Yox Date: Wed, 31 May 2023 20:39:14 -0600 Subject: [PATCH 6/9] Update arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt Co-authored-by: Simon Vergauwen --- .../src/commonMain/kotlin/arrow/core/raise/Builders.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt index 4b25d189c6a..ac74a7ab3fe 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt @@ -207,7 +207,7 @@ public class IorRaise @PublishedApi internal constructor( @RaiseDSL public inline fun recover( @BuilderInference block: IorRaise.() -> A, - @BuilderInference recover: (error: Error) -> A, + recover: (error: Error) -> A, ): A = when (val ior = ior(combineError, block)) { is Ior.Both -> { combine(ior.leftValue) From d4a771c288c8af9a6b3055ea5f2806cfc3ca054f Mon Sep 17 00:00:00 2001 From: James Yox Date: Wed, 31 May 2023 20:39:39 -0600 Subject: [PATCH 7/9] Update arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt Co-authored-by: Simon Vergauwen --- .../src/commonMain/kotlin/arrow/core/raise/Builders.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt index ac74a7ab3fe..f0aad9ad130 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt @@ -152,7 +152,7 @@ public class OptionRaise(private val raise: Raise) : Raise by raise @RaiseDSL public inline fun recover( @BuilderInference block: OptionRaise.() -> A, - @BuilderInference recover: () -> A, + recover: () -> A, ): A = when (val option = option(block)) { is None -> recover() is Some -> option.value From 4933c527409e356c8145ed724a040bf2cc1197e1 Mon Sep 17 00:00:00 2001 From: James Yox Date: Wed, 31 May 2023 20:39:47 -0600 Subject: [PATCH 8/9] Update arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt Co-authored-by: Simon Vergauwen --- .../src/commonMain/kotlin/arrow/core/raise/Builders.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt index f0aad9ad130..312cfe87e87 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt @@ -110,7 +110,7 @@ public class ResultRaise(private val raise: Raise) : Raise @RaiseDSL public inline fun recover( @BuilderInference block: ResultRaise.() -> A, - @BuilderInference recover: (Throwable) -> A, + recover: (Throwable) -> A, ): A = result(block).fold( onSuccess = { it }, onFailure = { recover(it) } From 9c02d3e3563cf080c407a24ffa7d0cebd1fd22c3 Mon Sep 17 00:00:00 2001 From: James Yox Date: Wed, 31 May 2023 20:39:55 -0600 Subject: [PATCH 9/9] Update arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt Co-authored-by: Simon Vergauwen --- .../src/commonMain/kotlin/arrow/core/raise/Builders.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt index 312cfe87e87..41f09a78ca7 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt @@ -77,7 +77,7 @@ public class NullableRaise(private val raise: Raise) : Raise by rais @RaiseDSL public inline fun recover( @BuilderInference block: NullableRaise.() -> A, - @BuilderInference recover: () -> A, + recover: () -> A, ): A = when (val nullable = nullable(block)) { null -> recover() else -> nullable