diff --git a/arrow-libs/core/arrow-core/api/arrow-core.api b/arrow-libs/core/arrow-core/api/arrow-core.api index 9fa5b825fb7..fbb6dd02f7c 100644 --- a/arrow-libs/core/arrow-core/api/arrow-core.api +++ b/arrow-libs/core/arrow-core/api/arrow-core.api @@ -2345,6 +2345,7 @@ public final class arrow/core/continuations/DefaultRaise : arrow/core/continuati public fun catch (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; 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 mapOrAccumulate (Ljava/lang/Iterable;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function2;)Ljava/util/List; public fun raise (Ljava/lang/Object;)Ljava/lang/Void; public fun recover (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public fun recover (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; @@ -2365,6 +2366,7 @@ public final class arrow/core/continuations/DefaultStateRaise : arrow/atomic/Ato public fun getValue ()Ljava/lang/Object; 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 mapOrAccumulate (Ljava/lang/Iterable;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function2;)Ljava/util/List; public fun raise (Ljava/lang/Object;)Ljava/lang/Void; public fun recover (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public fun recover (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; @@ -2430,6 +2432,7 @@ public final class arrow/core/continuations/IorRaise : arrow/core/continuations/ public final fun getEffect ()Larrow/core/continuations/StateRaise; 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 mapOrAccumulate (Ljava/lang/Iterable;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function2;)Ljava/util/List; public fun maybeCombine (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; public fun plus (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; public fun raise (Ljava/lang/Object;)Ljava/lang/Void; @@ -2468,6 +2471,8 @@ public final class arrow/core/continuations/NullableRaise : arrow/core/continuat public fun invoke (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static fun invoke-impl (Larrow/core/continuations/Raise;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public static fun invoke-impl (Larrow/core/continuations/Raise;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun mapOrAccumulate (Ljava/lang/Iterable;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function2;)Ljava/util/List; + public static fun mapOrAccumulate-impl (Larrow/core/continuations/Raise;Ljava/lang/Iterable;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function2;)Ljava/util/List; public synthetic fun raise (Ljava/lang/Object;)Ljava/lang/Void; public fun raise (Ljava/lang/Void;)Ljava/lang/Void; public static fun raise-impl (Larrow/core/continuations/Raise;Ljava/lang/Void;)Ljava/lang/Void; @@ -2511,6 +2516,8 @@ public final class arrow/core/continuations/OptionRaise : arrow/core/continuatio public fun invoke (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static fun invoke-impl (Larrow/core/continuations/Raise;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public static fun invoke-impl (Larrow/core/continuations/Raise;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun mapOrAccumulate (Ljava/lang/Iterable;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function2;)Ljava/util/List; + public static fun mapOrAccumulate-impl (Larrow/core/continuations/Raise;Ljava/lang/Iterable;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function2;)Ljava/util/List; public fun raise (Larrow/core/None;)Ljava/lang/Void; public synthetic fun raise (Ljava/lang/Object;)Ljava/lang/Void; public static fun raise-impl (Larrow/core/continuations/Raise;Larrow/core/None;)Ljava/lang/Void; @@ -2535,6 +2542,7 @@ public abstract interface class arrow/core/continuations/Raise { public abstract fun catch (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun invoke (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public abstract fun invoke (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun mapOrAccumulate (Ljava/lang/Iterable;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function2;)Ljava/util/List; public abstract fun raise (Ljava/lang/Object;)Ljava/lang/Void; public abstract fun recover (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public abstract fun recover (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; @@ -2551,6 +2559,7 @@ public final class arrow/core/continuations/Raise$DefaultImpls { public static fun catch (Larrow/core/continuations/Raise;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static fun invoke (Larrow/core/continuations/Raise;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public static fun invoke (Larrow/core/continuations/Raise;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static fun mapOrAccumulate (Larrow/core/continuations/Raise;Ljava/lang/Iterable;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function2;)Ljava/util/List; public static fun recover (Larrow/core/continuations/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public static fun recover (Larrow/core/continuations/Raise;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static fun recover (Larrow/core/continuations/Raise;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; @@ -2560,6 +2569,7 @@ public final class arrow/core/continuations/RaiseKt { public static final fun catch (Larrow/core/continuations/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public static final fun ensure (Larrow/core/continuations/Raise;ZLkotlin/jvm/functions/Function0;)V public static final fun ensureNotNull (Larrow/core/continuations/Raise;Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object; + public static final fun mapOrAccumulate (Larrow/core/continuations/Raise;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;)Ljava/util/List; public static final fun recover (Larrow/core/continuations/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public static final fun recover (Larrow/core/continuations/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; } @@ -2591,6 +2601,8 @@ public final class arrow/core/continuations/ResultRaise : arrow/core/continuatio public fun invoke (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static fun invoke-impl (Larrow/core/continuations/Raise;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public static fun invoke-impl (Larrow/core/continuations/Raise;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun mapOrAccumulate (Ljava/lang/Iterable;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function2;)Ljava/util/List; + public static fun mapOrAccumulate-impl (Larrow/core/continuations/Raise;Ljava/lang/Iterable;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function2;)Ljava/util/List; public synthetic fun raise (Ljava/lang/Object;)Ljava/lang/Void; public fun raise (Ljava/lang/Throwable;)Ljava/lang/Void; public static fun raise-impl (Larrow/core/continuations/Raise;Ljava/lang/Throwable;)Ljava/lang/Void; @@ -2618,6 +2630,7 @@ public final class arrow/core/continuations/StateRaise$DefaultImpls { public static fun catch (Larrow/core/continuations/StateRaise;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static fun invoke (Larrow/core/continuations/StateRaise;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public static fun invoke (Larrow/core/continuations/StateRaise;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static fun mapOrAccumulate (Larrow/core/continuations/StateRaise;Ljava/lang/Iterable;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function2;)Ljava/util/List; public static fun recover (Larrow/core/continuations/StateRaise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public static fun recover (Larrow/core/continuations/StateRaise;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static fun recover (Larrow/core/continuations/StateRaise;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Raise.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Raise.kt index 999d6f995d6..a080d046afb 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Raise.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Raise.kt @@ -3,10 +3,15 @@ package arrow.core.continuations import arrow.core.Either +import arrow.core.EmptyValue +import arrow.core.NonEmptyList import arrow.core.None import arrow.core.Option import arrow.core.Some +import arrow.core.emptyCombine import arrow.core.identity +import arrow.core.nel +import arrow.typeclasses.Semigroup import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract import kotlin.experimental.ExperimentalTypeInference @@ -61,6 +66,31 @@ public interface Raise { /** Raise a _logical failure_ of type [R] */ public fun raise(r: R): Nothing + + /** + * Accumulate the errors obtained by executing the [block] + * over every element of [this] using the given [semigroup]. + */ + public fun Iterable.mapOrAccumulate( + semigroup: Semigroup<@UnsafeVariance R>, + block: Raise.(A) -> B + ): List { + var error: Any? = EmptyValue + val results = mutableListOf() + forEach { + fold({ + block(it) + }, { newError -> + error = semigroup.emptyCombine(error, newError) + }, { + results.add(it) + }) + } + when (val e = error) { + is EmptyValue -> return results + else -> raise(EmptyValue.unbox(e)) + } + } /** * Invoke an [EagerEffect] inside `this` [Raise] context. @@ -247,3 +277,15 @@ public inline fun Raise.ensureNotNull(value: B?, raise: () -> R) contract { returns() implies (value != null) } return value ?: raise(raise()) } + +/** + * Accumulate the errors obtained by executing the [block] + * over every element of [list]. + */ +@EffectDSL +public inline fun Raise>.mapOrAccumulate( + list: Iterable, + crossinline block: Raise.(A) -> B +): List = list.mapOrAccumulate(Semigroup.nonEmptyList()) { + recover({ block(it) }, { raise(it.nel()) }) +} diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/continuations/EffectSpec.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/continuations/EffectSpec.kt index eb4b91b8c5c..1bc2e990a4c 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/continuations/EffectSpec.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/continuations/EffectSpec.kt @@ -1,6 +1,7 @@ package arrow.core.continuations import arrow.core.Either +import arrow.core.NonEmptyList import arrow.core.identity import arrow.core.left import arrow.core.right @@ -14,6 +15,7 @@ import io.kotest.property.arbitrary.arbitrary import io.kotest.property.arbitrary.boolean import io.kotest.property.arbitrary.flatMap import io.kotest.property.arbitrary.int +import io.kotest.property.arbitrary.list import io.kotest.property.arbitrary.long import io.kotest.property.arbitrary.orNull import io.kotest.property.arbitrary.string @@ -371,6 +373,22 @@ class EffectSpec : }.message.shouldNotBeNull() shouldBe msg2() } } + + "accumulate, returns every error" { + checkAll(Arb.list(Arb.int(), range = 2 .. 100)) { errors -> + either, List> { + mapOrAccumulate(errors) { raise(it) } + } shouldBe NonEmptyList.fromListUnsafe(errors).left() + } + } + + "accumulate, returns no error" { + checkAll(Arb.list(Arb.string())) { elements -> + either, List> { + mapOrAccumulate(elements) { it } + } shouldBe elements.right() + } + } }) private data class Failure(val msg: String)