diff --git a/arrow-libs/core/arrow-core/api/android/arrow-core.api b/arrow-libs/core/arrow-core/api/android/arrow-core.api index 4f419e93f2..bb37dc40e4 100644 --- a/arrow-libs/core/arrow-core/api/android/arrow-core.api +++ b/arrow-libs/core/arrow-core/api/android/arrow-core.api @@ -1002,7 +1002,7 @@ public class arrow/core/raise/RaiseAccumulate : arrow/core/raise/Accumulate, arr public final fun mapOrAccumulate-nfMsDo0 (Ljava/util/Set;Lkotlin/jvm/functions/Function2;)Ljava/util/Set; public final fun mapValuesOrAccumulate (Ljava/util/Map;Lkotlin/jvm/functions/Function2;)Ljava/util/Map; public fun raise (Ljava/lang/Object;)Ljava/lang/Void; - public final fun raiseErrors ()Ljava/lang/Void; + public final synthetic fun raiseErrors ()Ljava/lang/Void; public final fun withNel (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; } diff --git a/arrow-libs/core/arrow-core/api/jvm/arrow-core.api b/arrow-libs/core/arrow-core/api/jvm/arrow-core.api index 4f419e93f2..bb37dc40e4 100644 --- a/arrow-libs/core/arrow-core/api/jvm/arrow-core.api +++ b/arrow-libs/core/arrow-core/api/jvm/arrow-core.api @@ -1002,7 +1002,7 @@ public class arrow/core/raise/RaiseAccumulate : arrow/core/raise/Accumulate, arr public final fun mapOrAccumulate-nfMsDo0 (Ljava/util/Set;Lkotlin/jvm/functions/Function2;)Ljava/util/Set; public final fun mapValuesOrAccumulate (Ljava/util/Map;Lkotlin/jvm/functions/Function2;)Ljava/util/Map; public fun raise (Ljava/lang/Object;)Ljava/lang/Void; - public final fun raiseErrors ()Ljava/lang/Void; + public final synthetic fun raiseErrors ()Ljava/lang/Void; public final fun withNel (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; } 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 78452d42e9..ea25af3570 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 @@ -196,6 +196,7 @@ private class IorAccumulate( return raiseAccumulated } + @Deprecated("Binary compatibility", level = DeprecationLevel.ERROR) @ExperimentalRaiseAccumulateApi override val latestError: RaiseAccumulate.Value? get() = if (state.get() === EmptyValue) null else raiseAccumulated diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/RaiseAccumulate.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/RaiseAccumulate.kt index 6a18657988..efbf362e54 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/RaiseAccumulate.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/RaiseAccumulate.kt @@ -676,8 +676,13 @@ internal inline fun Raise>.forEachAccumulatingImp iterator: Iterator, @BuilderInference block: RaiseAccumulate.(item: A, hasErrors: Boolean) -> Unit ): Unit = accumulate { + var hasErrors = false iterator.forEach { - accumulating { block(it, hasAccumulatedErrors) } + accumulating { + block(it, hasErrors) + return@forEach // continue to next iteration since there were no errors + } + hasErrors = true } } @@ -848,11 +853,10 @@ public inline fun Raise>.accumulate( block: RaiseAccumulate.() -> A ): A { contract { callsInPlace(block, EXACTLY_ONCE) } - return with(RaiseAccumulate(this)) { - val result = block() - if (hasAccumulatedErrors) { latestError?.value } - result - } + val (raiseAccumulate, raiseErrorsIfAvailable) = RaiseAccumulate() + val result = block(raiseAccumulate) + raiseErrorsIfAvailable() + return result } @ExperimentalRaiseAccumulateApi @@ -888,7 +892,7 @@ public open class RaiseAccumulate @ExperimentalRaiseAccumulateApi constru public constructor(raise: Raise>) : this(ListAccumulate(raise)) @ExperimentalRaiseAccumulateApi - private constructor(listAccumulate: ListAccumulate) : this(listAccumulate, listAccumulate, listAccumulate::raiseSingle) + internal constructor(listAccumulate: ListAccumulate) : this(listAccumulate, listAccumulate, listAccumulate::raiseSingle) override fun raise(r: Error): Nothing = raiseErrorsWith(r) @@ -974,22 +978,20 @@ public open class RaiseAccumulate @ExperimentalRaiseAccumulateApi constru return block(raise) } - @OptIn(ExperimentalRaiseAccumulateApi::class) @PublishedApi @Deprecated("Binary compatibility", level = DeprecationLevel.HIDDEN) internal fun addErrors(newErrors: Iterable) { newErrors.toNonEmptyListOrNull()?.let(::accumulateAll) } - @OptIn(ExperimentalRaiseAccumulateApi::class) @PublishedApi @Deprecated("Binary compatibility", level = DeprecationLevel.HIDDEN) + @Suppress("DEPRECATION_ERROR") internal fun hasErrors(): Boolean = hasAccumulatedErrors - @Suppress("KotlinUnreachableCode") // wrong inspection - @OptIn(ExperimentalRaiseAccumulateApi::class) + @Suppress("KotlinUnreachableCode", "DEPRECATION_ERROR") // wrong inspection @PublishedApi - @Deprecated("Binary compatibility", level = DeprecationLevel.WARNING) + @Deprecated("Binary compatibility", level = DeprecationLevel.HIDDEN) internal fun raiseErrors(): Nothing = latestError?.value ?: error("No accumulated errors to raise") @Suppress("NOTHING_TO_INLINE") @@ -1005,8 +1007,8 @@ public open class RaiseAccumulate @ExperimentalRaiseAccumulateApi constru @PublishedApi internal class Error(private val raise: () -> Nothing) : Value() { - @OptIn(ExperimentalRaiseAccumulateApi::class) @Deprecated("Binary compatibility", level = DeprecationLevel.HIDDEN) + @Suppress("DEPRECATION_ERROR") constructor(raiseAccumulate: RaiseAccumulate<*>) : this({ raiseAccumulate.latestError?.value ?: error("No accumulated errors to raise") }) @@ -1094,8 +1096,15 @@ private class RaiseNel(private val accumulate: Accumulate) : Raise } } -@OptIn(ExperimentalRaiseAccumulateApi::class) -private class ListAccumulate(private val raise: Raise>) : Accumulate, Raise> { +@OptIn(PotentiallyUnsafeNonEmptyOperation::class) +@ExperimentalRaiseAccumulateApi +@PublishedApi +internal fun Raise>.RaiseAccumulate(): Pair, () -> Unit> = with(ListAccumulate(this)) { + RaiseAccumulate(this) to ::raiseErrorsIfAvailable +} + +@ExperimentalRaiseAccumulateApi +internal class ListAccumulate(private val raise: Raise>) : Accumulate, Raise> { private val list: MutableList = mutableListOf() fun raiseSingle(r: Error): Nothing = raise.raise(NonEmptyList(list + r)) @@ -1105,28 +1114,33 @@ private class ListAccumulate(private val raise: Raise // errors are never removed from `list`, so once this is valid, it stays valid private val error = Error { raise.raise(NonEmptyList(list)) } - @ExperimentalRaiseAccumulateApi + private val hasErrors get() = list.isNotEmpty() + + fun raiseErrorsIfAvailable() { + if (hasErrors) error.value + } + override fun accumulateAll(errors: NonEmptyList): Value { list.addAll(errors) return error } - @ExperimentalRaiseAccumulateApi - override val latestError: Value? get() = error.takeIf { list.isNotEmpty() } + @Deprecated("Binary compatibility", level = DeprecationLevel.ERROR) + override val latestError: Value? get() = error.takeIf { hasErrors } } -@OptIn(ExperimentalRaiseAccumulateApi::class) +@ExperimentalRaiseAccumulateApi private class TolerantAccumulate( private val underlying: Accumulate, private val raise: Raise> ) : Accumulate { - @ExperimentalRaiseAccumulateApi override fun accumulateAll(errors: NonEmptyList): Value { val error = underlying.accumulateAll(errors) return Error { raise.raise(error) } } - @ExperimentalRaiseAccumulateApi + @Deprecated("Binary compatibility", level = DeprecationLevel.ERROR) + @Suppress("DEPRECATION_ERROR") override val latestError: Value? get() { val error = underlying.latestError ?: return null return Error { raise.raise(error) } @@ -1148,11 +1162,14 @@ public interface Accumulate { @ExperimentalRaiseAccumulateApi public fun accumulateAll(errors: NonEmptyList): Value + @Deprecated("Binary compatibility", level = DeprecationLevel.ERROR) + @Suppress("DEPRECATION_ERROR") @ExperimentalRaiseAccumulateApi public val hasAccumulatedErrors: Boolean get() = latestError != null + @Deprecated("Binary compatibility", level = DeprecationLevel.ERROR) @ExperimentalRaiseAccumulateApi - public val latestError: Value? + public val latestError: Value? get() = throw UnsupportedOperationException() @ExperimentalRaiseAccumulateApi public fun Either.bindOrAccumulate(): Value = accumulating { bind() }