From aff56423053a52f3cbf7e26f716cb00aabfef917 Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Mon, 27 Feb 2023 10:53:31 +0100 Subject: [PATCH 01/19] Add missing mapOrAccumulate --- .../commonMain/kotlin/arrow/core/Iterable.kt | 71 +++++++++--- .../kotlin/arrow/core/NonEmptyList.kt | 101 +++++++++++++++--- .../commonMain/kotlin/arrow/core/Sequence.kt | 81 ++++++++++++-- .../commonMain/kotlin/arrow/core/Validated.kt | 2 + .../src/commonMain/kotlin/arrow/core/map.kt | 88 +++++++++++++-- 5 files changed, 292 insertions(+), 51 deletions(-) diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt index 89015278700..9816915cbb6 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt @@ -339,49 +339,64 @@ public inline fun Iterable.traverseValidated( ): Validated> = traverse(semigroup, f) +@Deprecated( + ValidatedDeprMsg + "Use the mapOrAccumulate API instead", + ReplaceWith( + "mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { f(it).bind() }.toValidated()", + "arrow.core.mapOrAccumulate" + ) +) @OptIn(ExperimentalTypeInference::class) @OverloadResolutionByLambdaReturnType public inline fun Iterable.traverse( semigroup: Semigroup, f: (A) -> Validated ): Validated> = - semigroup.run { - fold(Valid(ArrayList(collectionSizeOrDefault(10))) as Validated>) { acc, a -> - when (val res = f(a)) { - is Validated.Valid -> when (acc) { - is Valid -> acc.also { it.value.add(res.value) } - is Invalid -> acc - } - is Validated.Invalid -> when (acc) { - is Valid -> res - is Invalid -> Invalid(acc.value.combine(res.value)) - } - } - } - } + mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { f(it).bind() }.toValidated() @Deprecated("traverseValidated is being renamed to traverse to simplify the Arrow API", ReplaceWith("traverse(f)", "arrow.core.traverse")) public inline fun Iterable.traverseValidated(f: (A) -> ValidatedNel): ValidatedNel> = traverse(f) +@Deprecated( + ValidatedDeprMsg + "Use the mapOrAccumulate API instead", + ReplaceWith( + "mapOrAccumulate { f(it).bind() }.toValidated()", + "arrow.core.mapOrAccumulate" + ) +) @OptIn(ExperimentalTypeInference::class) @OverloadResolutionByLambdaReturnType public inline fun Iterable.traverse(f: (A) -> ValidatedNel): ValidatedNel> = - traverse(Semigroup.nonEmptyList(), f) + mapOrAccumulate { f(it).bind() }.toValidated() @Deprecated("sequenceValidated is being renamed to sequence to simplify the Arrow API", ReplaceWith("sequence(semigroup)", "arrow.core.sequence")) public fun Iterable>.sequenceValidated(semigroup: Semigroup): Validated> = sequence(semigroup) +@Deprecated( + ValidatedDeprMsg + "Use the mapOrAccumulate API instead", + ReplaceWith( + "mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { it.bind() }.toValidated()", + "arrow.core.mapOrAccumulate" + ) +) public fun Iterable>.sequence(semigroup: Semigroup): Validated> = - traverse(semigroup, ::identity) + mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { it.bind() }.toValidated() @Deprecated("sequenceValidated is being renamed to sequence to simplify the Arrow API", ReplaceWith("sequence()", "arrow.core.sequence")) public fun Iterable>.sequenceValidated(): ValidatedNel> = sequence() +@Deprecated( + ValidatedDeprMsg + "Use the mapOrAccumulate API instead", + ReplaceWith( + "mapOrAccumulate { it.bind() }.toValidated()", + "arrow.core.mapOrAccumulate" + ) +) public fun Iterable>.sequence(): ValidatedNel> = - traverse(Semigroup.nonEmptyList(), ::identity) + mapOrAccumulate { it.bind() }.toValidated() @Deprecated("traverseOption is being renamed to traverse to simplify the Arrow API", ReplaceWith("traverse(f)", "arrow.core.traverse")) public inline fun Iterable.traverseOption(f: (A) -> Option): Option> = @@ -481,6 +496,27 @@ public inline fun Iterable.mapOrAccumulate( return res.mapLeft { NonEmptyList(it[0], it.drop(1)) } } +@OptIn(ExperimentalTypeInference::class) +public inline fun Iterable.mapOrAccumulate( + @BuilderInference transform: Raise>.(A) -> B, +): Either, List> { + val buffer = mutableListOf() + val res = fold, ArrayList>>(Right(ArrayList(collectionSizeOrDefault(10)))) { acc, a -> + when (val res = either { transform(a) }) { + is Right -> when (acc) { + is Right -> acc.also { acc.value.add(res.value) } + is Left -> acc + } + + is Left -> when (acc) { + is Right -> Left(buffer.also { it.addAll(res.value) }) + is Left -> Left(buffer.also { it.addAll(res.value) }) + } + } + } + return res.mapLeft { NonEmptyList(it[0], it.drop(1)) } +} + /** * Flatten a list of [Either] into a single [Either] with a list of values, or accumulates all errors using [combine]. */ @@ -1109,6 +1145,7 @@ public fun Iterable>.separateEither(): Pair, List * @receiver Iterable of Validated * @return a tuple containing List with [Validated.Invalid] and another List with its [Validated.Valid] values. */ +@Deprecated("Use separateEither instead.", ReplaceWith("map { it.toEither() }.separateEither()")) public fun Iterable>.separateValidated(): Pair, List> { val invalids = ArrayList(collectionSizeOrDefault(10)) val valids = ArrayList(collectionSizeOrDefault(10)) diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt index 41ea03aced8..5b84b924117 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt @@ -1,7 +1,11 @@ +@file:OptIn(ExperimentalTypeInference::class) + package arrow.core import arrow.core.Either.Left import arrow.core.Either.Right +import arrow.core.raise.Raise +import arrow.core.raise.either import arrow.typeclasses.Semigroup import kotlin.experimental.ExperimentalTypeInference import kotlin.jvm.JvmStatic @@ -438,8 +442,11 @@ public fun NonEmptyList>.sequence(): Either semigroup.run { a.combine(b) } }) { f(it).bind() }.toValidated()", + "arrow.core.mapOrAccumulate" + ) ) public inline fun NonEmptyList.traverseValidated( semigroup: Semigroup, @@ -447,31 +454,95 @@ public inline fun NonEmptyList.traverseValidated( ): Validated> = traverse(semigroup, f) +@Deprecated( + ValidatedDeprMsg + "Use the mapOrAccumulate API instead", + ReplaceWith( + "mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { f(it).bind() }.toValidated()", + "arrow.core.mapOrAccumulate" + ) +) @OptIn(ExperimentalTypeInference::class) @OverloadResolutionByLambdaReturnType public inline fun NonEmptyList.traverse( semigroup: Semigroup, f: (A) -> Validated ): Validated> = - fold>>(mutableListOf().valid()) { acc, a -> - when (val res = f(a)) { - is Valid -> when (acc) { - is Valid -> acc.also { it.value.add(res.value) } - is Invalid -> acc + mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { f(it).bind() }.toValidated() + +@Deprecated( + ValidatedDeprMsg + "Use the mapOrAccumulate API instead", + ReplaceWith( + "mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { it.bind() }.toValidated()", + "arrow.core.mapOrAccumulate" + ) +) +public fun NonEmptyList>.sequenceValidated(semigroup: Semigroup): Validated> = + mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { it.bind() }.toValidated() + +@Deprecated( + ValidatedDeprMsg + "Use the mapOrAccumulate API instead", + ReplaceWith( + "mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { it.bind() }.toValidated()", + "arrow.core.mapOrAccumulate" + ) +) +public fun NonEmptyList>.sequence(semigroup: Semigroup): Validated> = + mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { it.bind() }.toValidated() + +public inline fun NonEmptyList.mapOrAccumulate( + combine: (E, E) -> E, + @BuilderInference transform: Raise.(A) -> B +): Either> = + fold>>(mutableListOf().right()) { acc, a -> + when (val res = either { transform(a) }) { + is Right -> when (acc) { + is Right -> acc.also { it.value.add(res.value) } + is Left -> acc } - is Invalid -> when (acc) { - is Valid -> res - is Invalid -> semigroup.run { Invalid(acc.value.combine(res.value)) } + is Left -> when (acc) { + is Right -> res + is Left -> Left(combine(acc.value, res.value)) } } }.map { requireNotNull(it.toNonEmptyListOrNull()) } -@Deprecated("sequenceValidated is being renamed to sequence to simplify the Arrow API", ReplaceWith("sequence()", "arrow.core.sequence")) -public fun NonEmptyList>.sequenceValidated(semigroup: Semigroup): Validated> = - sequence(semigroup) +public inline fun NonEmptyList.mapOrAccumulate( + @BuilderInference transform: Raise.(A) -> B +): Either, NonEmptyList> { + val buffer = mutableListOf() + return fold, MutableList>>(mutableListOf().right()) { acc, a -> + when (val res = either { transform(a) }) { + is Right -> when (acc) { + is Right -> acc.also { it.value.add(res.value) } + is Left -> acc + } -public fun NonEmptyList>.sequence(semigroup: Semigroup): Validated> = - traverse(semigroup, ::identity) + is Left -> when (acc) { + is Right -> Left(buffer.also { it.add(res.value) }) + is Left -> Left(buffer.also { it.add(res.value) }) + } + } + }.map { it.toNonEmptyListOrNull()!! }.mapLeft { it.toNonEmptyListOrNull()!! } +} + +public inline fun NonEmptyList.mapOrAccumulate( + @BuilderInference transform: Raise>.(A) -> B +): Either, NonEmptyList> { + val buffer = mutableListOf() + return fold, MutableList>>(mutableListOf().right()) { acc, a -> + when (val res = either { transform(a) }) { + is Right -> when (acc) { + is Right -> acc.also { it.value.add(res.value) } + is Left -> acc + } + + is Left -> when (acc) { + is Right -> Left(buffer.also { it.addAll(res.value) }) + is Left -> Left(buffer.also { it.addAll(res.value) }) + } + } + }.map { it.toNonEmptyListOrNull()!! }.mapLeft { it.toNonEmptyListOrNull()!! } +} @Deprecated( "traverseOption is being renamed to traverse to simplify the Arrow API", diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt index 7f67780684d..aec7a59ef9e 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt @@ -1,7 +1,11 @@ +@file:OptIn(ExperimentalTypeInference::class) + package arrow.core import arrow.core.Either.Left import arrow.core.Either.Right +import arrow.core.raise.Raise +import arrow.core.raise.either import arrow.typeclasses.Monoid import arrow.typeclasses.Semigroup import kotlin.experimental.ExperimentalTypeInference @@ -635,8 +639,15 @@ public fun Sequence>.sequence(): Option> = public fun Sequence>.sequenceOption(): Option> = sequence().map { it.asSequence() } +@Deprecated( + ValidatedDeprMsg + "Use the mapOrAccumulate API instead", + ReplaceWith( + "mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { it.bind() }.toValidated()", + "arrow.core.mapOrAccumulate" + ) +) public fun Sequence>.sequence(semigroup: Semigroup): Validated> = - traverse(semigroup, ::identity) + mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { it.bind() }.toValidated() @Deprecated( "sequenceValidated is being renamed to sequence to simplify the Arrow API", @@ -719,25 +730,77 @@ public fun Sequence.traverse(f: (A) -> Option): Option> { public fun Sequence.traverseOption(f: (A) -> Option): Option> = traverse(f).map { it.asSequence() } +@Deprecated( + ValidatedDeprMsg + "Use the mapOrAccumulate API instead", + ReplaceWith( + "mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { f(it).bind() }.toValidated()", + "arrow.core.mapOrAccumulate" + ) +) @OptIn(ExperimentalTypeInference::class) @OverloadResolutionByLambdaReturnType public fun Sequence.traverse( semigroup: Semigroup, f: (A) -> Validated ): Validated> = - fold(mutableListOf().valid() as Validated>) { acc, a -> - when (val res = f(a)) { - is Valid -> when (acc) { - is Valid -> acc.also { it.value.add(res.value) } - is Invalid -> acc + mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { f(it).bind() }.toValidated() + +public fun Sequence.mapOrAccumulate( + combine: (E, E) -> E, + @BuilderInference tranform: Raise.(A) -> B +): Either> = + fold>>(mutableListOf().right()) { acc, a -> + when (val res = either { tranform(a) }) { + is Right -> when (acc) { + is Right -> acc.also { it.value.add(res.value) } + is Left -> acc } - is Invalid -> when (acc) { - is Valid -> res - is Invalid -> semigroup.run { acc.value.combine(res.value).invalid() } + + is Left -> when (acc) { + is Right -> res + is Left -> combine(acc.value, res.value).left() } } } +public fun Sequence.mapOrAccumulate( + @BuilderInference tranform: Raise.(A) -> B +): Either, List> { + val buffer = mutableListOf() + return fold, MutableList>>(mutableListOf().right()) { acc, a -> + when (val res = either { tranform(a) }) { + is Right -> when (acc) { + is Right -> acc.also { it.value.add(res.value) } + is Left -> acc + } + + is Left -> when (acc) { + is Right -> Left(buffer.also { it.add(res.value) }) + is Left -> Left(buffer.also { it.add(res.value) }) + } + } + }.mapLeft { it.toNonEmptyListOrNull()!! } +} + +public fun Sequence.mapOrAccumulate( + @BuilderInference tranform: Raise>.(A) -> B +): Either, List> { + val buffer = mutableListOf() + return fold, MutableList>>(mutableListOf().right()) { acc, a -> + when (val res = either { tranform(a) }) { + is Right -> when (acc) { + is Right -> acc.also { it.value.add(res.value) } + is Left -> acc + } + + is Left -> when (acc) { + is Right -> Left(buffer.also { it.addAll(res.value) }) + is Left -> Left(buffer.also { it.addAll(res.value) }) + } + } + }.mapLeft { it.toNonEmptyListOrNull()!! } +} + @Deprecated( "traverseValidated is being renamed to traverse to simplify the Arrow API", ReplaceWith("traverse(semigroup, f).map { it.asSequence() }", "arrow.core.traverse") diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Validated.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Validated.kt index cf668335f19..79c82c3f0c5 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Validated.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Validated.kt @@ -854,3 +854,5 @@ public inline fun A.validNel(): ValidatedNel = public inline fun E.invalidNel(): ValidatedNel = Validated.invalidNel(this) + +internal const val ValidatedDeprMsg = "Validated functionally is being merged into Either.\n" diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/map.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/map.kt index 1265ec598c6..5b7d646e010 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/map.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/map.kt @@ -1,3 +1,5 @@ +@file:OptIn(ExperimentalTypeInference::class) + package arrow.core import arrow.core.Either.Left @@ -6,6 +8,8 @@ import arrow.typeclasses.Monoid import arrow.typeclasses.Semigroup import kotlin.experimental.ExperimentalTypeInference import kotlin.collections.flatMap as _flatMap +import arrow.core.raise.Raise +import arrow.core.raise.either /** * Combines to structures by taking the intersection of their shapes @@ -259,29 +263,86 @@ public fun Map>.sequence(): Either> = public fun Map>.sequenceEither(): Either> = sequence() -@Deprecated("traverseValidated is being renamed to traverse to simplify the Arrow API", ReplaceWith("traverse(semigroup, f)", "arrow.core.traverse")) +@Deprecated( + ValidatedDeprMsg + "Use the mapOrAccumulate API instead", + ReplaceWith( + "mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { f(it).bind() }.toValidated()", + "arrow.core.mapOrAccumulate" + ) +) public inline fun Map.traverseValidated( semigroup: Semigroup, f: (A) -> Validated ): Validated> = traverse(semigroup, f) +@Deprecated( + ValidatedDeprMsg + "Use the mapOrAccumulate API instead", + ReplaceWith( + "mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { f(it).bind() }.toValidated()", + "arrow.core.mapOrAccumulate" + ) +) public inline fun Map.traverse( semigroup: Semigroup, f: (A) -> Validated ): Validated> = - foldLeft(mutableMapOf().valid() as Validated>) { acc, (k, v) -> - when (val res = f(v)) { - is Valid -> when (acc) { - is Valid -> acc.also { it.value[k] = res.value } - is Invalid -> acc + mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { f(it).bind() }.toValidated() + +public inline fun Map.mapOrAccumulate( + combine: (E, E) -> E, + @BuilderInference transform: Raise.(A) -> B +): Either> = + fold>>(mutableMapOf().right() ) { acc, (k, v) -> + when (val res = either { transform(v) }) { + is Right -> when (acc) { + is Right -> acc.also { it.value[k] = res.value } + is Left -> acc } - is Invalid -> when (acc) { - is Valid -> res - is Invalid -> semigroup.run { acc.value.combine(res.value).invalid() } + + is Left -> when (acc) { + is Right -> res + is Left -> combine(acc.value, res.value).left() } } } - } + +public inline fun Map.mapOrAccumulate( + @BuilderInference transform: Raise.(A) -> B +): Either, Map> { + val buffer = mutableListOf() + return fold, MutableMap>>(mutableMapOf().right() ) { acc, (k, v) -> + when (val res = either { transform(v) }) { + is Right -> when (acc) { + is Right -> acc.also { it.value[k] = res.value } + is Left -> acc + } + + is Left -> when (acc) { + is Right -> Left(buffer.also { it.add(res.value) }) + is Left -> Left(buffer.also { it.add(res.value) }) + } + } + }.mapLeft { buffer.toNonEmptyListOrNull()!! } +} + +public inline fun Map.mapOrAccumulate( + @BuilderInference transform: Raise>.(A) -> B +): Either, Map> { + val buffer = mutableListOf() + return fold, MutableMap>>(mutableMapOf().right() ) { acc, (k, v) -> + when (val res = either { transform(v) }) { + is Right -> when (acc) { + is Right -> acc.also { it.value[k] = res.value } + is Left -> acc + } + + is Left -> when (acc) { + is Right -> Left(buffer.also { it.addAll(res.value) }) + is Left -> Left(buffer.also { it.addAll(res.value) }) + } + } + }.mapLeft { buffer.toNonEmptyListOrNull()!! } +} @Deprecated("sequenceValidated is being renamed to sequence to simplify the Arrow API", ReplaceWith("sequence(semigroup)", "arrow.core.sequence")) public fun Map>.sequenceValidated(semigroup: Semigroup): Validated> = @@ -514,6 +575,13 @@ public fun Map.combine(SG: Semigroup, b: Map): Map = public fun Iterable>.combineAll(SG: Semigroup): Map = fold(Monoid.map(SG)) +public inline fun Map.fold(initial: B, operation: (acc: B, Map.Entry) -> B): B { + var accumulator = initial + forEach { accumulator = operation(accumulator, it) } + return accumulator +} + +@Deprecated("Use fold instead align with Kotlin Std naming", ReplaceWith("fold(b, f)")) public inline fun Map.foldLeft(b: B, f: (B, Map.Entry) -> B): B { var result = b this.forEach { result = f(result, it) } From 3cd2fff61b9607e578d9bfb15621d66215fb9fb4 Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Mon, 27 Feb 2023 20:02:47 +0100 Subject: [PATCH 02/19] Emulate context receivers --- arrow-libs/core/arrow-core/api/arrow-core.api | 40 ++++ .../kotlin/arrow/core/AccumulatingRaise.kt | 132 ++++++++++++ .../commonMain/kotlin/arrow/core/Iterable.kt | 196 +++++++++--------- .../kotlin/arrow/core/NonEmptyList.kt | 53 +---- .../commonMain/kotlin/arrow/core/Sequence.kt | 71 ++----- .../src/commonMain/kotlin/arrow/core/map.kt | 66 ++---- .../api/arrow-fx-coroutines.api | 29 ++- .../kotlin/arrow/fx/coroutines/ParMap.kt | 21 +- 8 files changed, 349 insertions(+), 259 deletions(-) create mode 100644 arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/AccumulatingRaise.kt diff --git a/arrow-libs/core/arrow-core/api/arrow-core.api b/arrow-libs/core/arrow-core/api/arrow-core.api index 20eb0450c2a..d992849a80a 100644 --- a/arrow-libs/core/arrow-core/api/arrow-core.api +++ b/arrow-libs/core/arrow-core/api/arrow-core.api @@ -1,3 +1,35 @@ +public class arrow/core/AccumulatingRaise : arrow/core/raise/Raise { + public fun (Larrow/core/raise/Raise;)V + public fun attempt (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun bind (Larrow/core/Either;)Ljava/lang/Object; + public fun bind (Larrow/core/Option;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public fun bind (Larrow/core/Validated;)Ljava/lang/Object; + public fun bind (Larrow/core/continuations/EagerEffect;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun bind (Larrow/core/continuations/Effect;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun bind (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public fun bind (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public fun bind (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun bind1 (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun bindEager (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public final fun bindEither (Larrow/core/Either;)Ljava/lang/Object; + public final fun bindValidated (Larrow/core/Validated;)Ljava/lang/Object; + public fun catch (Larrow/core/continuations/Effect;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun catch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + 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 final fun invokeEager (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public final fun invokeEffect (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun raise (Larrow/core/NonEmptyList;)Ljava/lang/Void; + public synthetic fun raise (Ljava/lang/Object;)Ljava/lang/Void; + public final fun raise1 (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/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun recover (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun shift (Larrow/core/NonEmptyList;)Ljava/lang/Object; + public synthetic fun shift (Ljava/lang/Object;)Ljava/lang/Object; +} + public final class arrow/core/ArrowCoreInternalException : java/lang/RuntimeException { public static final field INSTANCE Larrow/core/ArrowCoreInternalException; public fun fillInStackTrace ()Ljava/lang/Throwable; @@ -620,6 +652,7 @@ public final class arrow/core/IterableKt { public static final fun firstOrNone (Ljava/lang/Iterable;)Larrow/core/Option; public static final fun firstOrNone (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Larrow/core/Option; public static final fun flatten (Ljava/lang/Iterable;)Ljava/util/List; + public static final fun flattenNelOrAccumulate (Ljava/lang/Iterable;)Larrow/core/Either; public static final fun flattenOption (Ljava/lang/Iterable;)Ljava/util/List; public static final fun flattenOrAccumulate (Ljava/lang/Iterable;)Larrow/core/Either; public static final fun flattenOrAccumulate (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;)Larrow/core/Either; @@ -702,8 +735,11 @@ public final class arrow/core/MapKt { public static final fun filterMap (Ljava/util/Map;Lkotlin/jvm/functions/Function1;)Ljava/util/Map; public static final fun filterOption (Ljava/util/Map;)Ljava/util/Map; public static final fun flatMap (Ljava/util/Map;Lkotlin/jvm/functions/Function1;)Ljava/util/Map; + public static final fun fold (Ljava/util/Map;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public static final fun foldLeft (Ljava/util/Map;Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public static final fun getOrNone (Ljava/util/Map;Ljava/lang/Object;)Larrow/core/Option; + public static final fun mapOrAccumulate (Ljava/util/Map;Lkotlin/jvm/functions/Function2;)Larrow/core/Either; + public static final fun mapOrAccumulate (Ljava/util/Map;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)Larrow/core/Either; public static final fun padZip (Ljava/util/Map;Ljava/util/Map;)Ljava/util/Map; public static final fun padZip (Ljava/util/Map;Ljava/util/Map;Lkotlin/jvm/functions/Function3;)Ljava/util/Map; public static final fun salign (Ljava/util/Map;Larrow/typeclasses/Semigroup;Ljava/util/Map;)Ljava/util/Map; @@ -794,6 +830,8 @@ public final class arrow/core/NonEmptyList$Companion { public final class arrow/core/NonEmptyListKt { public static final fun compareTo (Larrow/core/NonEmptyList;Larrow/core/NonEmptyList;)I public static final fun flatten (Larrow/core/NonEmptyList;)Larrow/core/NonEmptyList; + public static final fun mapOrAccumulate (Larrow/core/NonEmptyList;Lkotlin/jvm/functions/Function2;)Larrow/core/Either; + public static final fun mapOrAccumulate (Larrow/core/NonEmptyList;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)Larrow/core/Either; public static final fun max (Larrow/core/NonEmptyList;)Ljava/lang/Comparable; public static final fun maxBy (Larrow/core/NonEmptyList;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public static final fun min (Larrow/core/NonEmptyList;)Ljava/lang/Comparable; @@ -1547,6 +1585,8 @@ public final class arrow/core/SequenceKt { public static final fun leftPadZip (Lkotlin/sequences/Sequence;Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; public static final fun leftPadZip (Lkotlin/sequences/Sequence;Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function2;)Lkotlin/sequences/Sequence; public static final fun many (Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; + public static final fun mapOrAccumulate (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function2;)Larrow/core/Either; + public static final fun mapOrAccumulate (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)Larrow/core/Either; public static final fun once (Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; public static final fun padZip (Lkotlin/sequences/Sequence;Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; public static final fun padZip (Lkotlin/sequences/Sequence;Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function2;)Lkotlin/sequences/Sequence; diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/AccumulatingRaise.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/AccumulatingRaise.kt new file mode 100644 index 00000000000..cb8ca2b811b --- /dev/null +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/AccumulatingRaise.kt @@ -0,0 +1,132 @@ +@file:OptIn(ExperimentalTypeInference::class) + +package arrow.core + +import arrow.core.raise.EagerEffect +import arrow.core.raise.Effect +import arrow.core.raise.Raise +import arrow.core.raise.RaiseDSL +import arrow.core.raise.fold +import arrow.core.raise.recover +import kotlin.experimental.ExperimentalTypeInference +import kotlin.js.JsName +import kotlin.jvm.JvmInline +import kotlin.jvm.JvmName + +/** + * This is a temporary solution for accumulating errors in the context of a [Raise], + * belonging to [Iterable.mapOrAccumulate], [NonEmptyList.mapOrAccumulate], [Sequence.mapOrAccumulate] & [Map.mapOrAccumulate]. + * + * All methods that are defined in [AccumulatingRaise] can be implemented using context receivers instead, + * this can be [DeprecationLevel.HIDDEN] in a source -and binary-compatible way when context receivers are released: + * + * ```kotlin + * context(Raise>) + * fun Either.bind(): A = when (this) { + * is Invalid -> raise(nonEmptyListOf(value)) + * is Valid -> value + * } + * + * @OptIn(ExperimentalTypeInference::class) + * public inline fun Iterable.mapOrAccumulate( + * @BuilderInference transform: Raise>.(A) -> B, + * ): Either, List> + * ``` + */ +public open class AccumulatingRaise( + private val raise: Raise> +): Raise> by raise { + + @RaiseDSL + @JsName("raise1") + @JvmName("raise1") + public fun raise(r: E): Nothing = + raise.raise(nonEmptyListOf(r)) + + /** + * Invoke an [EagerEffect] inside `this` [Raise] context. + * Any _logical failure_ is raised in `this` [Raise] context, + * and thus short-circuits the computation. + * + * @see [recover] if you want to attempt to recover from any _logical failure_. + */ + @JsName("invokeEager") + @JvmName("invokeEager") + public operator fun EagerEffect.invoke(): A = + fold({ raise(it) }, ::identity) + + @RaiseDSL + @JsName("bindEager") + @JvmName("bindEager") + public fun EagerEffect.bind(): A = + fold({ raise(it) }, ::identity) + + /** + * Invoke an [Effect] inside `this` [Raise] context. + * Any _logical failure_ raised are raised in `this` [Raise] context, + * and thus short-circuits the computation. + * + * @see [recover] if you want to attempt to recover from any _logical failure_. + */ + @JsName("invokeEffect") + @JvmName("invokeEffect") + public suspend operator fun Effect.invoke(): A = + fold({ raise(it) }, ::identity) + + @RaiseDSL + @JsName("bind1") + @JvmName("bind1") + public suspend fun Effect.bind(): A = + fold({ raise(it) }, ::identity) + + @RaiseDSL + @JsName("bindEither") + @JvmName("bindEither") + public fun Either.bind(): A = when (this) { + is Either.Left -> raise(value) + is Either.Right -> value + } + + /* Will be removed in subsequent PRs for Arrow 2.x.x */ + @RaiseDSL + @JsName("bindValidated") + @JvmName("bindValidated") + public fun Validated.bind(): A = when (this) { + is Validated.Invalid -> raise(value) + is Validated.Valid -> value + } + +// @RaiseDSL +// public suspend infix fun Effect.recover( +// @BuilderInference resolve: suspend Raise>.(E) -> A +// ): A = +// fold({ this@recover.invoke(this) }, { throw it }, { resolve(it) }) { it } +// +// /** @see [recover] */ +// @RaiseDSL +// public infix fun EagerEffect.recover(@BuilderInference resolve: Raise>.(E) -> A): A = +// recover({ invoke() }, resolve) +// +// /** +// * Execute the [Effect] resulting in [A], +// * and recover from any _logical error_ of type [E], and [Throwable], by providing a fallback value of type [A], +// * or raising a new error of type [R]. +// * +// * @see [catch] if you don't need to recover from [Throwable]. +// */ +// @RaiseDSL +// public suspend fun Effect.recover( +// @BuilderInference recover: suspend Raise>.(E) -> A, +// @BuilderInference catch: suspend Raise>.(Throwable) -> A, +// ): A = fold({ invoke() }, { catch(it) }, { recover(it) }, { it }) +// +// @RaiseDSL +// public suspend infix fun Effect.catch( +// @BuilderInference catch: suspend Raise>.(Throwable) -> A, +// ): A = fold({ catch(it) }, { raise(it) }, { it }) +// +// @RaiseDSL +// public infix fun EagerEffect.catch( +// @BuilderInference catch: Raise>.(Throwable) -> A, +// ): A = fold({ catch(it) }, { raise(it) }, { it }) +} diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt index 9816915cbb6..ba1b92eedc2 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt @@ -5,11 +5,12 @@ package arrow.core import arrow.core.Either.Left import arrow.core.Either.Right import arrow.core.raise.Raise -import arrow.core.raise.either +import arrow.core.raise.fold import arrow.typeclasses.Monoid import arrow.typeclasses.Semigroup import kotlin.Result.Companion.success import kotlin.experimental.ExperimentalTypeInference +import kotlin.jvm.JvmName public inline fun Iterable.zip( c: Iterable, @@ -285,7 +286,10 @@ public inline fun Iterable.zip( internal fun Iterable.collectionSizeOrDefault(default: Int): Int = if (this is Collection<*>) this.size else default -@Deprecated("traverseEither is being renamed to traverse to simplify the Arrow API", ReplaceWith("traverse(f)", "arrow.core.traverse")) +@Deprecated( + "traverseEither is being renamed to traverse to simplify the Arrow API", + ReplaceWith("traverse(f)", "arrow.core.traverse") +) public inline fun Iterable.traverseEither(f: (A) -> Either): Either> = traverse(f) @@ -302,7 +306,10 @@ public inline fun Iterable.traverse(f: (A) -> Either): Either return destination.right() } -@Deprecated("sequenceEither is being renamed to sequence to simplify the Arrow API", ReplaceWith("sequence()", "arrow.core.sequence")) +@Deprecated( + "sequenceEither is being renamed to sequence to simplify the Arrow API", + ReplaceWith("sequence()", "arrow.core.sequence") +) public fun Iterable>.sequenceEither(): Either> = traverse(::identity) @@ -321,18 +328,27 @@ public inline fun Iterable.traverse(f: (A) -> Result): Result Iterable.traverseResult(f: (A) -> Result): Result> = traverse(f) -@Deprecated("sequenceResult is being renamed to sequence to simplify the Arrow API", ReplaceWith("sequence()", "arrow.core.sequence")) +@Deprecated( + "sequenceResult is being renamed to sequence to simplify the Arrow API", + ReplaceWith("sequence()", "arrow.core.sequence") +) public fun Iterable>.sequenceResult(): Result> = sequence() public fun Iterable>.sequence(): Result> = traverse(::identity) -@Deprecated("traverseValidated is being renamed to traverse to simplify the Arrow API", ReplaceWith("traverse(semigroup, f)", "arrow.core.traverse")) +@Deprecated( + "traverseValidated is being renamed to traverse to simplify the Arrow API", + ReplaceWith("traverse(semigroup, f)", "arrow.core.traverse") +) public inline fun Iterable.traverseValidated( semigroup: Semigroup, f: (A) -> Validated @@ -352,25 +368,31 @@ public inline fun Iterable.traverse( semigroup: Semigroup, f: (A) -> Validated ): Validated> = - mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { f(it).bind() }.toValidated() + mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { f(it).bind() }.toValidated() -@Deprecated("traverseValidated is being renamed to traverse to simplify the Arrow API", ReplaceWith("traverse(f)", "arrow.core.traverse")) +@Deprecated( + "traverseValidated is being renamed to traverse to simplify the Arrow API", + ReplaceWith("traverse(f)", "arrow.core.traverse") +) public inline fun Iterable.traverseValidated(f: (A) -> ValidatedNel): ValidatedNel> = traverse(f) @Deprecated( ValidatedDeprMsg + "Use the mapOrAccumulate API instead", ReplaceWith( - "mapOrAccumulate { f(it).bind() }.toValidated()", + "mapOrAccumulate { f(it).bind() }.toValidated()", "arrow.core.mapOrAccumulate" ) ) @OptIn(ExperimentalTypeInference::class) @OverloadResolutionByLambdaReturnType public inline fun Iterable.traverse(f: (A) -> ValidatedNel): ValidatedNel> = - mapOrAccumulate { f(it).bind() }.toValidated() + mapOrAccumulate { f(it).bind() }.toValidated() -@Deprecated("sequenceValidated is being renamed to sequence to simplify the Arrow API", ReplaceWith("sequence(semigroup)", "arrow.core.sequence")) +@Deprecated( + "sequenceValidated is being renamed to sequence to simplify the Arrow API", + ReplaceWith("sequence(semigroup)", "arrow.core.sequence") +) public fun Iterable>.sequenceValidated(semigroup: Semigroup): Validated> = sequence(semigroup) @@ -382,23 +404,29 @@ public fun Iterable>.sequenceValidated(semigroup: Semigro ) ) public fun Iterable>.sequence(semigroup: Semigroup): Validated> = - mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { it.bind() }.toValidated() + mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { it.bind() }.toValidated() -@Deprecated("sequenceValidated is being renamed to sequence to simplify the Arrow API", ReplaceWith("sequence()", "arrow.core.sequence")) +@Deprecated( + "sequenceValidated is being renamed to sequence to simplify the Arrow API", + ReplaceWith("sequence()", "arrow.core.sequence") +) public fun Iterable>.sequenceValidated(): ValidatedNel> = sequence() @Deprecated( ValidatedDeprMsg + "Use the mapOrAccumulate API instead", ReplaceWith( - "mapOrAccumulate { it.bind() }.toValidated()", + "mapOrAccumulate, A> { it.bind() }.toValidated()", "arrow.core.mapOrAccumulate" ) ) public fun Iterable>.sequence(): ValidatedNel> = - mapOrAccumulate { it.bind() }.toValidated() + mapOrAccumulate, A> { it.bind() }.toValidated() -@Deprecated("traverseOption is being renamed to traverse to simplify the Arrow API", ReplaceWith("traverse(f)", "arrow.core.traverse")) +@Deprecated( + "traverseOption is being renamed to traverse to simplify the Arrow API", + ReplaceWith("traverse(f)", "arrow.core.traverse") +) public inline fun Iterable.traverseOption(f: (A) -> Option): Option> = traverse(f) @@ -415,14 +443,20 @@ public inline fun Iterable.traverse(f: (A) -> Option): Option Iterable>.sequenceOption(): Option> = sequence() public fun Iterable>.sequence(): Option> = traverse(::identity) -@Deprecated("traverseNullable is being renamed to traverse to simplify the Arrow API", ReplaceWith("traverse(f)", "arrow.core.traverse")) +@Deprecated( + "traverseNullable is being renamed to traverse to simplify the Arrow API", + ReplaceWith("traverse(f)", "arrow.core.traverse") +) public inline fun Iterable.traverseNullable(f: (A) -> B?): List? = traverse(f) @@ -441,7 +475,10 @@ public inline fun Iterable.traverse(f: (A) -> B?): List? { return acc.toList() } -@Deprecated("sequenceNullable is being renamed to sequence to simplify the Arrow API", ReplaceWith("sequence()", "arrow.core.sequence")) +@Deprecated( + "sequenceNullable is being renamed to sequence to simplify the Arrow API", + ReplaceWith("sequence()", "arrow.core.sequence") +) public fun Iterable.sequenceNullable(): List? = sequence() @@ -455,20 +492,13 @@ public fun Iterable.sequenceNullable(): List? = public inline fun Iterable.mapOrAccumulate( combine: (Error, Error) -> Error, @BuilderInference transform: Raise.(A) -> B, -): Either> = - fold>>(Right(ArrayList(collectionSizeOrDefault(10)))) { acc, a -> - when (val res = either { transform(a) }) { - is Right -> when (acc) { - is Right -> acc.also { acc.value.add(res.value) } - is Left -> acc - } - - is Left -> when (acc) { - is Right -> res - is Left -> Left(combine(acc.value, res.value)) - } - } - } +): Either> { + var left: Any? = EmptyValue + val right = ArrayList(collectionSizeOrDefault(10)) + for (item in this) + fold({ transform(item) }, { error -> left = EmptyValue.combine(left, error, combine) }, { b -> right.add(b) }) + return if (left !== EmptyValue) EmptyValue.unbox(left).left() else right.right() +} /** * Returns [Either] a [List] containing the results of applying the given [transform] function @@ -477,83 +507,56 @@ public inline fun Iterable.mapOrAccumulate( */ @OptIn(ExperimentalTypeInference::class) public inline fun Iterable.mapOrAccumulate( - @BuilderInference transform: Raise.(A) -> B, + @BuilderInference transform: AccumulatingRaise.(A) -> B, ): Either, List> { - val buffer = mutableListOf() - val res = fold, ArrayList>>(Right(ArrayList(collectionSizeOrDefault(10)))) { acc, a -> - when (val res = either { transform(a) }) { - is Right -> when (acc) { - is Right -> acc.also { acc.value.add(res.value) } - is Left -> acc - } - - is Left -> when (acc) { - is Right -> Left(buffer.also { it.add(res.value) }) - is Left -> Left(buffer.also { it.add(res.value) }) - } - } - } - return res.mapLeft { NonEmptyList(it[0], it.drop(1)) } -} - -@OptIn(ExperimentalTypeInference::class) -public inline fun Iterable.mapOrAccumulate( - @BuilderInference transform: Raise>.(A) -> B, -): Either, List> { - val buffer = mutableListOf() - val res = fold, ArrayList>>(Right(ArrayList(collectionSizeOrDefault(10)))) { acc, a -> - when (val res = either { transform(a) }) { - is Right -> when (acc) { - is Right -> acc.also { acc.value.add(res.value) } - is Left -> acc - } - - is Left -> when (acc) { - is Right -> Left(buffer.also { it.addAll(res.value) }) - is Left -> Left(buffer.also { it.addAll(res.value) }) - } - } - } - return res.mapLeft { NonEmptyList(it[0], it.drop(1)) } + val left = mutableListOf() + val right = ArrayList(collectionSizeOrDefault(10)) + for (item in this) + fold({ transform(AccumulatingRaise(this), item) }, { error -> left.addAll(error) }, { b -> right.add(b) }) + return left.toNonEmptyListOrNull()?.left() ?: right.right() } /** * Flatten a list of [Either] into a single [Either] with a list of values, or accumulates all errors using [combine]. */ -public inline fun Iterable>.flattenOrAccumulate(combine: (Error, Error) -> Error): Either> = - fold, Either>>(Right(ArrayList(collectionSizeOrDefault(10)))) { acc, res -> - when (res) { - is Right -> when (acc) { - is Right -> acc.also { acc.value.add(res.value) } - is Left -> acc - } - - is Left -> when (acc) { - is Right -> res - is Left -> Left(combine(acc.value, res.value)) - } +public inline fun Iterable>.flattenOrAccumulate(combine: (Error, Error) -> Error): Either> { + var left: Any? = EmptyValue + val right = ArrayList(collectionSizeOrDefault(10)) + for (item in this) { + when (item) { + is Right -> right.add(item.value) + is Left -> left = EmptyValue.combine(left, item.value, combine) } } + return if (left !== EmptyValue) EmptyValue.unbox(left).left() else right.right() +} /** * Flatten a list of [Either] into a single [Either] with a list of values, or accumulates all errors with into an [NonEmptyList]. */ public fun Iterable>.flattenOrAccumulate(): Either, List> { - val buffer = mutableListOf() - val res = fold, Either, ArrayList>>(Right(ArrayList(collectionSizeOrDefault(10)))) { acc, res -> - when (res) { - is Right -> when (acc) { - is Right -> acc.also { acc.value.add(res.value) } - is Left -> acc - } + val left = mutableListOf() + val right = ArrayList(collectionSizeOrDefault(10)) + for (item in this) { + when (item) { + is Right -> right.add(item.value) + is Left -> left.add(item.value) + } + } + return left.toNonEmptyListOrNull()?.left() ?: right.right() +} - is Left -> when (acc) { - is Right -> Left(buffer.also { it.add(res.value) }) - is Left -> Left(buffer.also { it.add(res.value) }) - } +@JvmName("flattenNelOrAccumulate") +public fun Iterable>.flattenOrAccumulate(): Either, List> { + val left = mutableListOf() + val right = ArrayList(collectionSizeOrDefault(10)) + for (item in this) { + when (item) { + is Right -> right.add(item.value) + is Left -> left.addAll(item.value) } } - return res.mapLeft { NonEmptyList(it[0], it.drop(1)) } + return left.toNonEmptyListOrNull()?.left() ?: right.right() } public fun Iterable.sequence(): List? = @@ -912,6 +915,7 @@ public fun Iterable.firstOrNone(): Option = } else { None } + else -> { iterator().nextOrNone() } @@ -945,6 +949,7 @@ public fun Iterable.singleOrNone(): Option = 1 -> firstOrNone() else -> None } + else -> { iterator().run { nextOrNone().filter { !hasNext() } } } @@ -976,6 +981,7 @@ public fun Iterable.lastOrNone(): Option = } else { None } + else -> iterator().run { if (hasNext()) { var last: T @@ -1010,6 +1016,7 @@ public fun Iterable.elementAtOrNone(index: Int): Option = in indices -> Some(elementAt(index)) else -> None } + else -> iterator().skip(index).nextOrNone() } @@ -1019,6 +1026,7 @@ private tailrec fun Iterator.skip(count: Int): Iterator = next() skip(count - 1) } + else -> this } diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt index 5b84b924117..7935c08e7e9 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt @@ -8,6 +8,7 @@ import arrow.core.raise.Raise import arrow.core.raise.either import arrow.typeclasses.Semigroup import kotlin.experimental.ExperimentalTypeInference +import kotlin.jvm.JvmName import kotlin.jvm.JvmStatic public typealias Nel = NonEmptyList @@ -493,56 +494,12 @@ public inline fun NonEmptyList.mapOrAccumulate( combine: (E, E) -> E, @BuilderInference transform: Raise.(A) -> B ): Either> = - fold>>(mutableListOf().right()) { acc, a -> - when (val res = either { transform(a) }) { - is Right -> when (acc) { - is Right -> acc.also { it.value.add(res.value) } - is Left -> acc - } - is Left -> when (acc) { - is Right -> res - is Left -> Left(combine(acc.value, res.value)) - } - } - }.map { requireNotNull(it.toNonEmptyListOrNull()) } - -public inline fun NonEmptyList.mapOrAccumulate( - @BuilderInference transform: Raise.(A) -> B -): Either, NonEmptyList> { - val buffer = mutableListOf() - return fold, MutableList>>(mutableListOf().right()) { acc, a -> - when (val res = either { transform(a) }) { - is Right -> when (acc) { - is Right -> acc.also { it.value.add(res.value) } - is Left -> acc - } - - is Left -> when (acc) { - is Right -> Left(buffer.also { it.add(res.value) }) - is Left -> Left(buffer.also { it.add(res.value) }) - } - } - }.map { it.toNonEmptyListOrNull()!! }.mapLeft { it.toNonEmptyListOrNull()!! } -} + all.mapOrAccumulate(combine, transform).map { requireNotNull(it.toNonEmptyListOrNull()) } public inline fun NonEmptyList.mapOrAccumulate( - @BuilderInference transform: Raise>.(A) -> B -): Either, NonEmptyList> { - val buffer = mutableListOf() - return fold, MutableList>>(mutableListOf().right()) { acc, a -> - when (val res = either { transform(a) }) { - is Right -> when (acc) { - is Right -> acc.also { it.value.add(res.value) } - is Left -> acc - } - - is Left -> when (acc) { - is Right -> Left(buffer.also { it.addAll(res.value) }) - is Left -> Left(buffer.also { it.addAll(res.value) }) - } - } - }.map { it.toNonEmptyListOrNull()!! }.mapLeft { it.toNonEmptyListOrNull()!! } -} + @BuilderInference transform: AccumulatingRaise.(A) -> B +): Either, NonEmptyList> = + all.mapOrAccumulate(transform).map { requireNotNull(it.toNonEmptyListOrNull()) } @Deprecated( "traverseOption is being renamed to traverse to simplify the Arrow API", diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt index aec7a59ef9e..78e684a2f7a 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt @@ -6,9 +6,11 @@ import arrow.core.Either.Left import arrow.core.Either.Right import arrow.core.raise.Raise import arrow.core.raise.either +import arrow.core.raise.fold import arrow.typeclasses.Monoid import arrow.typeclasses.Semigroup import kotlin.experimental.ExperimentalTypeInference +import kotlin.jvm.JvmName public fun Sequence.zip( c: Sequence, @@ -745,60 +747,25 @@ public fun Sequence.traverse( ): Validated> = mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { f(it).bind() }.toValidated() -public fun Sequence.mapOrAccumulate( - combine: (E, E) -> E, - @BuilderInference tranform: Raise.(A) -> B -): Either> = - fold>>(mutableListOf().right()) { acc, a -> - when (val res = either { tranform(a) }) { - is Right -> when (acc) { - is Right -> acc.also { it.value.add(res.value) } - is Left -> acc - } - - is Left -> when (acc) { - is Right -> res - is Left -> combine(acc.value, res.value).left() - } - } - } - -public fun Sequence.mapOrAccumulate( - @BuilderInference tranform: Raise.(A) -> B -): Either, List> { - val buffer = mutableListOf() - return fold, MutableList>>(mutableListOf().right()) { acc, a -> - when (val res = either { tranform(a) }) { - is Right -> when (acc) { - is Right -> acc.also { it.value.add(res.value) } - is Left -> acc - } - - is Left -> when (acc) { - is Right -> Left(buffer.also { it.add(res.value) }) - is Left -> Left(buffer.also { it.add(res.value) }) - } - } - }.mapLeft { it.toNonEmptyListOrNull()!! } +public fun Sequence.mapOrAccumulate( + combine: (Error, Error) -> Error, + @BuilderInference transform: Raise.(A) -> B +): Either> { + var left: Any? = EmptyValue + val right = mutableListOf() + for (item in this) + fold({ transform(item) }, { error -> left = EmptyValue.combine(left, error, combine) }, { b -> right.add(b) }) + return if (left !== EmptyValue) EmptyValue.unbox(left).left() else right.right() } -public fun Sequence.mapOrAccumulate( - @BuilderInference tranform: Raise>.(A) -> B -): Either, List> { - val buffer = mutableListOf() - return fold, MutableList>>(mutableListOf().right()) { acc, a -> - when (val res = either { tranform(a) }) { - is Right -> when (acc) { - is Right -> acc.also { it.value.add(res.value) } - is Left -> acc - } - - is Left -> when (acc) { - is Right -> Left(buffer.also { it.addAll(res.value) }) - is Left -> Left(buffer.also { it.addAll(res.value) }) - } - } - }.mapLeft { it.toNonEmptyListOrNull()!! } +public fun Sequence.mapOrAccumulate( + @BuilderInference transform: AccumulatingRaise.(A) -> B +): Either, List> { + val left = mutableListOf() + val right = mutableListOf() + for (item in this) + fold({ transform(AccumulatingRaise(this), item) }, { error -> left.addAll(error) }, { b -> right.add(b) }) + return left.toNonEmptyListOrNull()?.left() ?: right.right() } @Deprecated( diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/map.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/map.kt index 5b7d646e010..d92efde5735 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/map.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/map.kt @@ -9,7 +9,7 @@ import arrow.typeclasses.Semigroup import kotlin.experimental.ExperimentalTypeInference import kotlin.collections.flatMap as _flatMap import arrow.core.raise.Raise -import arrow.core.raise.either +import arrow.core.raise.fold /** * Combines to structures by taking the intersection of their shapes @@ -279,7 +279,7 @@ public inline fun Map.traverseValidated( @Deprecated( ValidatedDeprMsg + "Use the mapOrAccumulate API instead", ReplaceWith( - "mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { f(it).bind() }.toValidated()", + "mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { f(it.value).bind() }.toValidated()", "arrow.core.mapOrAccumulate" ) ) @@ -287,61 +287,27 @@ public inline fun Map.traverse( semigroup: Semigroup, f: (A) -> Validated ): Validated> = - mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { f(it).bind() }.toValidated() + mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { f(it.value).bind() }.toValidated() public inline fun Map.mapOrAccumulate( combine: (E, E) -> E, - @BuilderInference transform: Raise.(A) -> B -): Either> = - fold>>(mutableMapOf().right() ) { acc, (k, v) -> - when (val res = either { transform(v) }) { - is Right -> when (acc) { - is Right -> acc.also { it.value[k] = res.value } - is Left -> acc - } - - is Left -> when (acc) { - is Right -> res - is Left -> combine(acc.value, res.value).left() } - } - } - -public inline fun Map.mapOrAccumulate( - @BuilderInference transform: Raise.(A) -> B -): Either, Map> { - val buffer = mutableListOf() - return fold, MutableMap>>(mutableMapOf().right() ) { acc, (k, v) -> - when (val res = either { transform(v) }) { - is Right -> when (acc) { - is Right -> acc.also { it.value[k] = res.value } - is Left -> acc - } - - is Left -> when (acc) { - is Right -> Left(buffer.also { it.add(res.value) }) - is Left -> Left(buffer.also { it.add(res.value) }) - } - } - }.mapLeft { buffer.toNonEmptyListOrNull()!! } + @BuilderInference transform: Raise.(Map.Entry) -> B +): Either> { + var left: Any? = EmptyValue + val right = mutableMapOf() + for (element in this) + fold({ transform(element) }, { left = EmptyValue.combine(left, it, combine) }, { right[element.key] = it }) + return if (left !== EmptyValue) EmptyValue.unbox(left).left() else right.right() } public inline fun Map.mapOrAccumulate( - @BuilderInference transform: Raise>.(A) -> B + @BuilderInference transform: AccumulatingRaise.(Map.Entry) -> B ): Either, Map> { - val buffer = mutableListOf() - return fold, MutableMap>>(mutableMapOf().right() ) { acc, (k, v) -> - when (val res = either { transform(v) }) { - is Right -> when (acc) { - is Right -> acc.also { it.value[k] = res.value } - is Left -> acc - } - - is Left -> when (acc) { - is Right -> Left(buffer.also { it.addAll(res.value) }) - is Left -> Left(buffer.also { it.addAll(res.value) }) - } - } - }.mapLeft { buffer.toNonEmptyListOrNull()!! } + val left = mutableListOf() + val right = mutableMapOf() + for (element in this) + fold({ transform(AccumulatingRaise(this), element) }, { error -> left.addAll(error) }, { right[element.key] = it }) + return left.toNonEmptyListOrNull()?.left() ?: right.right() } @Deprecated("sequenceValidated is being renamed to sequence to simplify the Arrow API", ReplaceWith("sequence(semigroup)", "arrow.core.sequence")) diff --git a/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api b/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api index 60ca7e688f3..1c884ae3fcb 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api +++ b/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api @@ -1,3 +1,8 @@ +public final class arrow/fx/coroutines/AccumulatingScopedRaise : arrow/core/AccumulatingRaise, kotlinx/coroutines/CoroutineScope { + public fun (Larrow/core/raise/Raise;Lkotlinx/coroutines/CoroutineScope;)V + public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext; +} + public abstract interface class arrow/fx/coroutines/Atomic { public static final field Companion Larrow/fx/coroutines/Atomic$Companion; public abstract fun access (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; @@ -574,22 +579,28 @@ public final class arrow/fx/coroutines/ScheduleKt { public static final fun retryOrElseEither (Larrow/fx/coroutines/Schedule;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } -public final class arrow/fx/coroutines/ScopedRaise : arrow/core/continuations/EffectScope, kotlinx/coroutines/CoroutineScope { - public fun (Larrow/core/continuations/EffectScope;Lkotlinx/coroutines/CoroutineScope;)V +public final class arrow/fx/coroutines/ScopedRaise : arrow/core/raise/Raise, kotlinx/coroutines/CoroutineScope { + public fun (Larrow/core/raise/Raise;Lkotlinx/coroutines/CoroutineScope;)V public fun attempt (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun bind (Larrow/core/Either;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun bind (Larrow/core/Option;Lkotlin/jvm/functions/Function0;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun bind (Larrow/core/Validated;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun bind (Larrow/core/Either;)Ljava/lang/Object; + public fun bind (Larrow/core/Option;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public fun bind (Larrow/core/Validated;)Ljava/lang/Object; public fun bind (Larrow/core/continuations/EagerEffect;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun bind (Larrow/core/continuations/Effect;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun bind (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun bind (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun bind (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public fun bind (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public fun bind (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun catch (Larrow/core/continuations/Effect;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun catch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public fun catch (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun ensure (ZLkotlin/jvm/functions/Function0;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext; + 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 fun recover (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public fun recover (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun shift (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun recover (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun shift (Ljava/lang/Object;)Ljava/lang/Object; } public final class arrow/fx/coroutines/Use { diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParMap.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParMap.kt index 751402ea818..23bb5a5a8dd 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParMap.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParMap.kt @@ -1,10 +1,13 @@ package arrow.fx.coroutines +import arrow.core.AccumulatingRaise import arrow.core.Either import arrow.core.NonEmptyList import arrow.core.continuations.EffectScope import arrow.core.continuations.either import arrow.core.flattenOrAccumulate +import arrow.core.raise.Raise +import arrow.core.raise.either import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll @@ -34,9 +37,9 @@ public suspend fun Iterable.parMap( /** Temporary intersection type, until we have context receivers */ public class ScopedRaise( - raise: EffectScope, + raise: Raise, scope: CoroutineScope -) : CoroutineScope by scope, EffectScope by raise +) : CoroutineScope by scope, Raise by raise public suspend fun Iterable.parMapOrAccumulate( context: CoroutineContext = EmptyCoroutineContext, @@ -72,10 +75,16 @@ public suspend fun Iterable.parMapOrAccumulate( }.awaitAll().flattenOrAccumulate(combine) } +/** Temporary intersection type, until we have context receivers */ +public class AccumulatingScopedRaise( + raise: Raise>, + scope: CoroutineScope +) : CoroutineScope by scope, AccumulatingRaise(raise) + public suspend fun Iterable.parMapOrAccumulate( context: CoroutineContext = EmptyCoroutineContext, concurrency: Int, - transform: suspend ScopedRaise.(A) -> B + transform: suspend AccumulatingScopedRaise.(A) -> B ): Either, List> = coroutineScope { val semaphore = Semaphore(concurrency) @@ -83,7 +92,7 @@ public suspend fun Iterable.parMapOrAccumulate( async(context) { either { semaphore.withPermit { - transform(ScopedRaise(this, this@coroutineScope), it) + transform(AccumulatingScopedRaise(this, this@coroutineScope), it) } } } @@ -92,13 +101,13 @@ public suspend fun Iterable.parMapOrAccumulate( public suspend fun Iterable.parMapOrAccumulate( context: CoroutineContext = EmptyCoroutineContext, - transform: suspend ScopedRaise.(A) -> B + transform: suspend AccumulatingScopedRaise.(A) -> B ): Either, List> = coroutineScope { map { async(context) { either { - transform(ScopedRaise(this, this@coroutineScope), it) + transform(AccumulatingScopedRaise(this, this@coroutineScope), it) } } }.awaitAll().flattenOrAccumulate() From 4c7e98ce835daac0e88b1ab185c4e69961f44f13 Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Mon, 27 Feb 2023 20:35:15 +0100 Subject: [PATCH 03/19] Fix ParMapTest.kt --- .../src/commonTest/kotlin/arrow/fx/coroutines/ParMapTest.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParMapTest.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParMapTest.kt index 8c000f4b090..62946e3d91a 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParMapTest.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParMapTest.kt @@ -1,9 +1,11 @@ package arrow.fx.coroutines import arrow.core.Either +import arrow.core.EitherNel import arrow.core.NonEmptyList import arrow.core.continuations.either import arrow.core.left +import arrow.core.nonEmptyListOf import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.should import io.kotest.matchers.shouldBe @@ -143,7 +145,7 @@ class ParMapTest : StringSpec({ "parMapOrAccumulate accumulates shifts" { checkAll(Arb.string()) { e -> (0 until 100).parMapOrAccumulate { _ -> - shift(e) + raise(e) } shouldBe NonEmptyList(e, (1 until 100).map { e }).left() } } From fb668e8411518e4358c70f9f9511cbf950c66d16 Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Tue, 28 Feb 2023 09:20:05 +0100 Subject: [PATCH 04/19] Allow binding ValidatedNel inside mapOrAccumulate(combine, transform) --- .../kotlin/arrow/core/AccumulatingRaise.kt | 75 ++++++++++--------- .../commonMain/kotlin/arrow/core/Iterable.kt | 21 +++++- .../kotlin/arrow/fx/coroutines/ParMap.kt | 30 +++----- 3 files changed, 70 insertions(+), 56 deletions(-) diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/AccumulatingRaise.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/AccumulatingRaise.kt index cb8ca2b811b..abf154fe4f6 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/AccumulatingRaise.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/AccumulatingRaise.kt @@ -74,8 +74,8 @@ public open class AccumulatingRaise( fold({ raise(it) }, ::identity) @RaiseDSL - @JsName("bind1") - @JvmName("bind1") + @JsName("bindEffect") + @JvmName("bindEffect") public suspend fun Effect.bind(): A = fold({ raise(it) }, ::identity) @@ -96,37 +96,42 @@ public open class AccumulatingRaise( is Validated.Valid -> value } -// @RaiseDSL -// public suspend infix fun Effect.recover( -// @BuilderInference resolve: suspend Raise>.(E) -> A -// ): A = -// fold({ this@recover.invoke(this) }, { throw it }, { resolve(it) }) { it } -// -// /** @see [recover] */ -// @RaiseDSL -// public infix fun EagerEffect.recover(@BuilderInference resolve: Raise>.(E) -> A): A = -// recover({ invoke() }, resolve) -// -// /** -// * Execute the [Effect] resulting in [A], -// * and recover from any _logical error_ of type [E], and [Throwable], by providing a fallback value of type [A], -// * or raising a new error of type [R]. -// * -// * @see [catch] if you don't need to recover from [Throwable]. -// */ -// @RaiseDSL -// public suspend fun Effect.recover( -// @BuilderInference recover: suspend Raise>.(E) -> A, -// @BuilderInference catch: suspend Raise>.(Throwable) -> A, -// ): A = fold({ invoke() }, { catch(it) }, { recover(it) }, { it }) -// -// @RaiseDSL -// public suspend infix fun Effect.catch( -// @BuilderInference catch: suspend Raise>.(Throwable) -> A, -// ): A = fold({ catch(it) }, { raise(it) }, { it }) -// -// @RaiseDSL -// public infix fun EagerEffect.catch( -// @BuilderInference catch: Raise>.(Throwable) -> A, -// ): A = fold({ catch(it) }, { raise(it) }, { it }) + @RaiseDSL + @JvmName("recoverEffect") + public suspend infix fun Effect.recover( + @BuilderInference resolve: suspend Raise>.(E) -> A + ): A = + fold({ this@recover.invoke(this) }, { throw it }, { resolve(it) }) { it } + + /** @see [recover] */ + @RaiseDSL + @JvmName("recoverEager") + public infix fun EagerEffect.recover(@BuilderInference resolve: Raise>.(E) -> A): A = + recover({ invoke() }, resolve) + + /** + * Execute the [Effect] resulting in [A], + * and recover from any _logical error_ of type [E], and [Throwable], by providing a fallback value of type [A], + * or raising a new error of type [R]. + * + * @see [catch] if you don't need to recover from [Throwable]. + */ + @RaiseDSL + @JvmName("recoverAndCatchEffect") + public suspend fun Effect.recover( + @BuilderInference recover: suspend Raise>.(E) -> A, + @BuilderInference catch: suspend Raise>.(Throwable) -> A, + ): A = fold({ invoke() }, { catch(it) }, { recover(it) }, { it }) + + @RaiseDSL + @JvmName("catchEffect") + public suspend infix fun Effect.catch( + @BuilderInference catch: suspend Raise>.(Throwable) -> A, + ): A = fold({ catch(it) }, { raise(it) }, { it }) + + @RaiseDSL + @JvmName("catchEagerEffect") + public infix fun EagerEffect.catch( + @BuilderInference catch: Raise>.(Throwable) -> A, + ): A = fold({ catch(it) }, { raise(it) }, { it }) } diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt index ba1b92eedc2..fcde5e9979b 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt @@ -491,12 +491,16 @@ public fun Iterable.sequenceNullable(): List? = @OptIn(ExperimentalTypeInference::class) public inline fun Iterable.mapOrAccumulate( combine: (Error, Error) -> Error, - @BuilderInference transform: Raise.(A) -> B, + @BuilderInference transform: AccumulatingRaise.(A) -> B, ): Either> { var left: Any? = EmptyValue val right = ArrayList(collectionSizeOrDefault(10)) for (item in this) - fold({ transform(item) }, { error -> left = EmptyValue.combine(left, error, combine) }, { b -> right.add(b) }) + fold( + { transform(AccumulatingRaise(this), item) }, + { error -> left = EmptyValue.combine(left, error.reduce(combine), combine) }, + { b -> right.add(b) } + ) return if (left !== EmptyValue) EmptyValue.unbox(left).left() else right.right() } @@ -531,6 +535,19 @@ public inline fun Iterable>.flattenOrAccumulate(comb return if (left !== EmptyValue) EmptyValue.unbox(left).left() else right.right() } +@JvmName("flattenNelOrAccumulate") +public fun Iterable>.flattenOrAccumulate(combine: (Error, Error) -> Error): Either> { + var left: Any? = EmptyValue + val right = ArrayList(collectionSizeOrDefault(10)) + for (item in this) { + when (item) { + is Right -> right.add(item.value) + is Left -> left = EmptyValue.combine(left, item.value.reduce(combine), combine) + } + } + return if (left !== EmptyValue) EmptyValue.unbox(left).left() else right.right() +} + /** * Flatten a list of [Either] into a single [Either] with a list of values, or accumulates all errors with into an [NonEmptyList]. */ diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParMap.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParMap.kt index 23bb5a5a8dd..bef18090c63 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParMap.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParMap.kt @@ -3,8 +3,6 @@ package arrow.fx.coroutines import arrow.core.AccumulatingRaise import arrow.core.Either import arrow.core.NonEmptyList -import arrow.core.continuations.EffectScope -import arrow.core.continuations.either import arrow.core.flattenOrAccumulate import arrow.core.raise.Raise import arrow.core.raise.either @@ -36,16 +34,16 @@ public suspend fun Iterable.parMap( } /** Temporary intersection type, until we have context receivers */ -public class ScopedRaise( - raise: Raise, +public class ScopedAccumulatingRaise( + raise: Raise>, scope: CoroutineScope -) : CoroutineScope by scope, Raise by raise +) : CoroutineScope by scope, AccumulatingRaise(raise) public suspend fun Iterable.parMapOrAccumulate( context: CoroutineContext = EmptyCoroutineContext, concurrency: Int, combine: (Error, Error) -> Error, - transform: suspend ScopedRaise.(A) -> B + transform: suspend ScopedAccumulatingRaise.(A) -> B ): Either> = coroutineScope { val semaphore = Semaphore(concurrency) @@ -53,7 +51,7 @@ public suspend fun Iterable.parMapOrAccumulate( async(context) { either { semaphore.withPermit { - transform(ScopedRaise(this, this@coroutineScope), it) + transform(ScopedAccumulatingRaise(this, this@coroutineScope), it) } } } @@ -63,28 +61,22 @@ public suspend fun Iterable.parMapOrAccumulate( public suspend fun Iterable.parMapOrAccumulate( context: CoroutineContext = EmptyCoroutineContext, combine: (Error, Error) -> Error, - transform: suspend ScopedRaise.(A) -> B + transform: suspend ScopedAccumulatingRaise.(A) -> B ): Either> = coroutineScope { map { async(context) { either { - transform(ScopedRaise(this, this@coroutineScope), it) + transform(ScopedAccumulatingRaise(this, this@coroutineScope), it) } } }.awaitAll().flattenOrAccumulate(combine) } -/** Temporary intersection type, until we have context receivers */ -public class AccumulatingScopedRaise( - raise: Raise>, - scope: CoroutineScope -) : CoroutineScope by scope, AccumulatingRaise(raise) - public suspend fun Iterable.parMapOrAccumulate( context: CoroutineContext = EmptyCoroutineContext, concurrency: Int, - transform: suspend AccumulatingScopedRaise.(A) -> B + transform: suspend ScopedAccumulatingRaise.(A) -> B ): Either, List> = coroutineScope { val semaphore = Semaphore(concurrency) @@ -92,7 +84,7 @@ public suspend fun Iterable.parMapOrAccumulate( async(context) { either { semaphore.withPermit { - transform(AccumulatingScopedRaise(this, this@coroutineScope), it) + transform(ScopedAccumulatingRaise(this, this@coroutineScope), it) } } } @@ -101,13 +93,13 @@ public suspend fun Iterable.parMapOrAccumulate( public suspend fun Iterable.parMapOrAccumulate( context: CoroutineContext = EmptyCoroutineContext, - transform: suspend AccumulatingScopedRaise.(A) -> B + transform: suspend ScopedAccumulatingRaise.(A) -> B ): Either, List> = coroutineScope { map { async(context) { either { - transform(AccumulatingScopedRaise(this, this@coroutineScope), it) + transform(ScopedAccumulatingRaise(this, this@coroutineScope), it) } } }.awaitAll().flattenOrAccumulate() From bd95a653d57a70865329e6a637dd69564ce26b8f Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Tue, 28 Feb 2023 09:37:26 +0100 Subject: [PATCH 05/19] apiDump, and update other mapOrAccumulate --- arrow-libs/core/arrow-core/api/arrow-core.api | 8 +++++- .../commonMain/kotlin/arrow/core/Iterable.kt | 2 +- .../kotlin/arrow/core/NonEmptyList.kt | 2 +- .../commonMain/kotlin/arrow/core/Sequence.kt | 6 ++--- .../src/commonMain/kotlin/arrow/core/map.kt | 8 ++++-- .../api/arrow-fx-coroutines.api | 26 +------------------ 6 files changed, 19 insertions(+), 33 deletions(-) diff --git a/arrow-libs/core/arrow-core/api/arrow-core.api b/arrow-libs/core/arrow-core/api/arrow-core.api index d992849a80a..eb658d7c41f 100644 --- a/arrow-libs/core/arrow-core/api/arrow-core.api +++ b/arrow-libs/core/arrow-core/api/arrow-core.api @@ -9,13 +9,15 @@ public class arrow/core/AccumulatingRaise : arrow/core/raise/Raise { public fun bind (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public fun bind (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public fun bind (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun bind1 (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public final fun bindEager (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public final fun bindEffect (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public final fun bindEither (Larrow/core/Either;)Ljava/lang/Object; public final fun bindValidated (Larrow/core/Validated;)Ljava/lang/Object; public fun catch (Larrow/core/continuations/Effect;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun catch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public fun catch (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun catchEagerEffect (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public final fun catchEffect (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 final fun invokeEager (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; @@ -26,6 +28,9 @@ public class arrow/core/AccumulatingRaise : arrow/core/raise/Raise { public fun recover (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public fun recover (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun recover (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun recoverAndCatchEffect (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun recoverEager (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public final fun recoverEffect (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun shift (Larrow/core/NonEmptyList;)Ljava/lang/Object; public synthetic fun shift (Ljava/lang/Object;)Ljava/lang/Object; } @@ -653,6 +658,7 @@ public final class arrow/core/IterableKt { public static final fun firstOrNone (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Larrow/core/Option; public static final fun flatten (Ljava/lang/Iterable;)Ljava/util/List; public static final fun flattenNelOrAccumulate (Ljava/lang/Iterable;)Larrow/core/Either; + public static final fun flattenNelOrAccumulate (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;)Larrow/core/Either; public static final fun flattenOption (Ljava/lang/Iterable;)Ljava/util/List; public static final fun flattenOrAccumulate (Ljava/lang/Iterable;)Larrow/core/Either; public static final fun flattenOrAccumulate (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;)Larrow/core/Either; diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt index fcde5e9979b..cff4f1f3525 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt @@ -498,7 +498,7 @@ public inline fun Iterable.mapOrAccumulate( for (item in this) fold( { transform(AccumulatingRaise(this), item) }, - { error -> left = EmptyValue.combine(left, error.reduce(combine), combine) }, + { errors -> left = EmptyValue.combine(left, errors.reduce(combine), combine) }, { b -> right.add(b) } ) return if (left !== EmptyValue) EmptyValue.unbox(left).left() else right.right() diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt index 7935c08e7e9..7dce9dfe839 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt @@ -492,7 +492,7 @@ public fun NonEmptyList>.sequence(semigroup: Semigroup public inline fun NonEmptyList.mapOrAccumulate( combine: (E, E) -> E, - @BuilderInference transform: Raise.(A) -> B + @BuilderInference transform: AccumulatingRaise.(A) -> B ): Either> = all.mapOrAccumulate(combine, transform).map { requireNotNull(it.toNonEmptyListOrNull()) } diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt index 78e684a2f7a..b344ab569df 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt @@ -749,12 +749,12 @@ public fun Sequence.traverse( public fun Sequence.mapOrAccumulate( combine: (Error, Error) -> Error, - @BuilderInference transform: Raise.(A) -> B + @BuilderInference transform: AccumulatingRaise.(A) -> B ): Either> { var left: Any? = EmptyValue val right = mutableListOf() for (item in this) - fold({ transform(item) }, { error -> left = EmptyValue.combine(left, error, combine) }, { b -> right.add(b) }) + fold({ transform(AccumulatingRaise(this), item) }, { errors -> left = EmptyValue.combine(left, errors.reduce(combine), combine) }, { b -> right.add(b) }) return if (left !== EmptyValue) EmptyValue.unbox(left).left() else right.right() } @@ -764,7 +764,7 @@ public fun Sequence.mapOrAccumulate( val left = mutableListOf() val right = mutableListOf() for (item in this) - fold({ transform(AccumulatingRaise(this), item) }, { error -> left.addAll(error) }, { b -> right.add(b) }) + fold({ transform(AccumulatingRaise(this), item) }, { errors -> left.addAll(errors) }, { b -> right.add(b) }) return left.toNonEmptyListOrNull()?.left() ?: right.right() } diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/map.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/map.kt index d92efde5735..279af414755 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/map.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/map.kt @@ -291,12 +291,16 @@ public inline fun Map.traverse( public inline fun Map.mapOrAccumulate( combine: (E, E) -> E, - @BuilderInference transform: Raise.(Map.Entry) -> B + @BuilderInference transform: AccumulatingRaise.(Map.Entry) -> B ): Either> { var left: Any? = EmptyValue val right = mutableMapOf() for (element in this) - fold({ transform(element) }, { left = EmptyValue.combine(left, it, combine) }, { right[element.key] = it }) + fold( + { transform(AccumulatingRaise(this), element) }, + { errors -> left = EmptyValue.combine(left, errors.reduce(combine), combine) }, + { right[element.key] = it } + ) return if (left !== EmptyValue) EmptyValue.unbox(left).left() else right.right() } diff --git a/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api b/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api index 1c884ae3fcb..e77dd6bb784 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api +++ b/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api @@ -1,8 +1,3 @@ -public final class arrow/fx/coroutines/AccumulatingScopedRaise : arrow/core/AccumulatingRaise, kotlinx/coroutines/CoroutineScope { - public fun (Larrow/core/raise/Raise;Lkotlinx/coroutines/CoroutineScope;)V - public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext; -} - public abstract interface class arrow/fx/coroutines/Atomic { public static final field Companion Larrow/fx/coroutines/Atomic$Companion; public abstract fun access (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; @@ -579,28 +574,9 @@ public final class arrow/fx/coroutines/ScheduleKt { public static final fun retryOrElseEither (Larrow/fx/coroutines/Schedule;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } -public final class arrow/fx/coroutines/ScopedRaise : arrow/core/raise/Raise, kotlinx/coroutines/CoroutineScope { +public final class arrow/fx/coroutines/ScopedAccumulatingRaise : arrow/core/AccumulatingRaise, kotlinx/coroutines/CoroutineScope { public fun (Larrow/core/raise/Raise;Lkotlinx/coroutines/CoroutineScope;)V - public fun attempt (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun bind (Larrow/core/Either;)Ljava/lang/Object; - public fun bind (Larrow/core/Option;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; - public fun bind (Larrow/core/Validated;)Ljava/lang/Object; - public fun bind (Larrow/core/continuations/EagerEffect;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun bind (Larrow/core/continuations/Effect;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun bind (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; - public fun bind (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; - public fun bind (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun catch (Larrow/core/continuations/Effect;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun catch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; - public fun catch (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext; - 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 fun recover (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; - public fun recover (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun recover (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun shift (Ljava/lang/Object;)Ljava/lang/Object; } public final class arrow/fx/coroutines/Use { From f41908ef624933a1d512f74aed3aa8df81f6618f Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Fri, 3 Mar 2023 13:34:19 +0100 Subject: [PATCH 06/19] Fix recover conflict --- .../src/commonMain/kotlin/arrow/core/AccumulatingRaise.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/AccumulatingRaise.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/AccumulatingRaise.kt index abf154fe4f6..3390dfe500b 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/AccumulatingRaise.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/AccumulatingRaise.kt @@ -107,7 +107,7 @@ public open class AccumulatingRaise( @RaiseDSL @JvmName("recoverEager") public infix fun EagerEffect.recover(@BuilderInference resolve: Raise>.(E) -> A): A = - recover({ invoke() }, resolve) + recover({ invoke() }) { resolve(it) } /** * Execute the [Effect] resulting in [A], From 8ee244643f62c2bfc253f73c5b20e6469239410e Mon Sep 17 00:00:00 2001 From: nomisRev Date: Fri, 3 Mar 2023 12:39:41 +0000 Subject: [PATCH 07/19] Update API files --- arrow-libs/core/arrow-core/api/arrow-core.api | 2 -- 1 file changed, 2 deletions(-) diff --git a/arrow-libs/core/arrow-core/api/arrow-core.api b/arrow-libs/core/arrow-core/api/arrow-core.api index 2f5e7643f89..0ef796fb092 100644 --- a/arrow-libs/core/arrow-core/api/arrow-core.api +++ b/arrow-libs/core/arrow-core/api/arrow-core.api @@ -2,11 +2,9 @@ public class arrow/core/AccumulatingRaise : arrow/core/raise/Raise { public fun (Larrow/core/raise/Raise;)V public fun attempt (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun bind (Larrow/core/Either;)Ljava/lang/Object; - public fun bind (Larrow/core/Option;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public fun bind (Larrow/core/Validated;)Ljava/lang/Object; public fun bind (Larrow/core/continuations/EagerEffect;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun bind (Larrow/core/continuations/Effect;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun bind (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public fun bind (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public fun bind (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public final fun bindEager (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; From 285f5d7d954976937061c6e6e5ab1bab867676d2 Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Fri, 3 Mar 2023 15:32:05 +0100 Subject: [PATCH 08/19] Flatten RaiseAccumulate in arrow.core.raise --- arrow-libs/core/arrow-core/api/arrow-core.api | 70 ++++----- .../kotlin/arrow/core/AccumulatingRaise.kt | 137 ------------------ .../commonMain/kotlin/arrow/core/Iterable.kt | 10 +- .../kotlin/arrow/core/NonEmptyList.kt | 8 +- .../commonMain/kotlin/arrow/core/Sequence.kt | 12 +- .../src/commonMain/kotlin/arrow/core/map.kt | 10 +- .../arrow/core/raise/RaiseAccumulate.kt | 127 ++++++++++++++++ .../api/arrow-fx-coroutines.api | 2 +- .../kotlin/arrow/fx/coroutines/ParMap.kt | 22 +-- 9 files changed, 192 insertions(+), 206 deletions(-) delete mode 100644 arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/AccumulatingRaise.kt diff --git a/arrow-libs/core/arrow-core/api/arrow-core.api b/arrow-libs/core/arrow-core/api/arrow-core.api index 0ef796fb092..eb2f90cec21 100644 --- a/arrow-libs/core/arrow-core/api/arrow-core.api +++ b/arrow-libs/core/arrow-core/api/arrow-core.api @@ -1,38 +1,3 @@ -public class arrow/core/AccumulatingRaise : arrow/core/raise/Raise { - public fun (Larrow/core/raise/Raise;)V - public fun attempt (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun bind (Larrow/core/Either;)Ljava/lang/Object; - public fun bind (Larrow/core/Validated;)Ljava/lang/Object; - public fun bind (Larrow/core/continuations/EagerEffect;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun bind (Larrow/core/continuations/Effect;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun bind (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; - public fun bind (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun bindEager (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; - public final fun bindEffect (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun bindEither (Larrow/core/Either;)Ljava/lang/Object; - public final fun bindValidated (Larrow/core/Validated;)Ljava/lang/Object; - public fun catch (Larrow/core/continuations/Effect;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun catch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; - public fun catch (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun catchEagerEffect (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; - public final fun catchEffect (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 final fun invokeEager (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; - public final fun invokeEffect (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun raise (Larrow/core/NonEmptyList;)Ljava/lang/Void; - public synthetic fun raise (Ljava/lang/Object;)Ljava/lang/Void; - public final fun raise1 (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/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun recover (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun recoverAndCatchEffect (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun recoverEager (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; - public final fun recoverEffect (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun shift (Larrow/core/NonEmptyList;)Ljava/lang/Object; - public synthetic fun shift (Ljava/lang/Object;)Ljava/lang/Object; -} - public final class arrow/core/ArrowCoreInternalException : java/lang/RuntimeException { public static final field INSTANCE Larrow/core/ArrowCoreInternalException; public fun fillInStackTrace ()Ljava/lang/Throwable; @@ -3440,6 +3405,41 @@ public final class arrow/core/raise/Raise$DefaultImpls { public static fun shift (Larrow/core/raise/Raise;Ljava/lang/Object;)Ljava/lang/Object; } +public class arrow/core/raise/RaiseAccumulate : arrow/core/raise/Raise { + public fun (Larrow/core/raise/Raise;)V + public fun attempt (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun bind (Larrow/core/Either;)Ljava/lang/Object; + public fun bind (Larrow/core/Validated;)Ljava/lang/Object; + public fun bind (Larrow/core/continuations/EagerEffect;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun bind (Larrow/core/continuations/Effect;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun bind (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public fun bind (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun bindEager (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public final fun bindEffect (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun bindEither (Larrow/core/Either;)Ljava/lang/Object; + public final fun bindValidated (Larrow/core/Validated;)Ljava/lang/Object; + public fun catch (Larrow/core/continuations/Effect;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun catch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public fun catch (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun catchEagerEffect (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public final fun catchEffect (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 final fun invokeEager (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public final fun invokeEffect (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun raise (Larrow/core/NonEmptyList;)Ljava/lang/Void; + public synthetic fun raise (Ljava/lang/Object;)Ljava/lang/Void; + public final fun raise1 (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/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun recover (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun recoverAndCatchEffect (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun recoverEager (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public final fun recoverEffect (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun shift (Larrow/core/NonEmptyList;)Ljava/lang/Object; + public synthetic fun shift (Ljava/lang/Object;)Ljava/lang/Object; +} + public abstract interface annotation class arrow/core/raise/RaiseDSL : java/lang/annotation/Annotation { } diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/AccumulatingRaise.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/AccumulatingRaise.kt deleted file mode 100644 index 3390dfe500b..00000000000 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/AccumulatingRaise.kt +++ /dev/null @@ -1,137 +0,0 @@ -@file:OptIn(ExperimentalTypeInference::class) - -package arrow.core - -import arrow.core.raise.EagerEffect -import arrow.core.raise.Effect -import arrow.core.raise.Raise -import arrow.core.raise.RaiseDSL -import arrow.core.raise.fold -import arrow.core.raise.recover -import kotlin.experimental.ExperimentalTypeInference -import kotlin.js.JsName -import kotlin.jvm.JvmInline -import kotlin.jvm.JvmName - -/** - * This is a temporary solution for accumulating errors in the context of a [Raise], - * belonging to [Iterable.mapOrAccumulate], [NonEmptyList.mapOrAccumulate], [Sequence.mapOrAccumulate] & [Map.mapOrAccumulate]. - * - * All methods that are defined in [AccumulatingRaise] can be implemented using context receivers instead, - * this can be [DeprecationLevel.HIDDEN] in a source -and binary-compatible way when context receivers are released: - * - * ```kotlin - * context(Raise>) - * fun Either.bind(): A = when (this) { - * is Invalid -> raise(nonEmptyListOf(value)) - * is Valid -> value - * } - * - * @OptIn(ExperimentalTypeInference::class) - * public inline fun Iterable.mapOrAccumulate( - * @BuilderInference transform: Raise>.(A) -> B, - * ): Either, List> - * ``` - */ -public open class AccumulatingRaise( - private val raise: Raise> -): Raise> by raise { - - @RaiseDSL - @JsName("raise1") - @JvmName("raise1") - public fun raise(r: E): Nothing = - raise.raise(nonEmptyListOf(r)) - - /** - * Invoke an [EagerEffect] inside `this` [Raise] context. - * Any _logical failure_ is raised in `this` [Raise] context, - * and thus short-circuits the computation. - * - * @see [recover] if you want to attempt to recover from any _logical failure_. - */ - @JsName("invokeEager") - @JvmName("invokeEager") - public operator fun EagerEffect.invoke(): A = - fold({ raise(it) }, ::identity) - - @RaiseDSL - @JsName("bindEager") - @JvmName("bindEager") - public fun EagerEffect.bind(): A = - fold({ raise(it) }, ::identity) - - /** - * Invoke an [Effect] inside `this` [Raise] context. - * Any _logical failure_ raised are raised in `this` [Raise] context, - * and thus short-circuits the computation. - * - * @see [recover] if you want to attempt to recover from any _logical failure_. - */ - @JsName("invokeEffect") - @JvmName("invokeEffect") - public suspend operator fun Effect.invoke(): A = - fold({ raise(it) }, ::identity) - - @RaiseDSL - @JsName("bindEffect") - @JvmName("bindEffect") - public suspend fun Effect.bind(): A = - fold({ raise(it) }, ::identity) - - @RaiseDSL - @JsName("bindEither") - @JvmName("bindEither") - public fun Either.bind(): A = when (this) { - is Either.Left -> raise(value) - is Either.Right -> value - } - - /* Will be removed in subsequent PRs for Arrow 2.x.x */ - @RaiseDSL - @JsName("bindValidated") - @JvmName("bindValidated") - public fun Validated.bind(): A = when (this) { - is Validated.Invalid -> raise(value) - is Validated.Valid -> value - } - - @RaiseDSL - @JvmName("recoverEffect") - public suspend infix fun Effect.recover( - @BuilderInference resolve: suspend Raise>.(E) -> A - ): A = - fold({ this@recover.invoke(this) }, { throw it }, { resolve(it) }) { it } - - /** @see [recover] */ - @RaiseDSL - @JvmName("recoverEager") - public infix fun EagerEffect.recover(@BuilderInference resolve: Raise>.(E) -> A): A = - recover({ invoke() }) { resolve(it) } - - /** - * Execute the [Effect] resulting in [A], - * and recover from any _logical error_ of type [E], and [Throwable], by providing a fallback value of type [A], - * or raising a new error of type [R]. - * - * @see [catch] if you don't need to recover from [Throwable]. - */ - @RaiseDSL - @JvmName("recoverAndCatchEffect") - public suspend fun Effect.recover( - @BuilderInference recover: suspend Raise>.(E) -> A, - @BuilderInference catch: suspend Raise>.(Throwable) -> A, - ): A = fold({ invoke() }, { catch(it) }, { recover(it) }, { it }) - - @RaiseDSL - @JvmName("catchEffect") - public suspend infix fun Effect.catch( - @BuilderInference catch: suspend Raise>.(Throwable) -> A, - ): A = fold({ catch(it) }, { raise(it) }, { it }) - - @RaiseDSL - @JvmName("catchEagerEffect") - public infix fun EagerEffect.catch( - @BuilderInference catch: Raise>.(Throwable) -> A, - ): A = fold({ catch(it) }, { raise(it) }, { it }) -} diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt index cff4f1f3525..3363c5524cb 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt @@ -4,7 +4,7 @@ package arrow.core import arrow.core.Either.Left import arrow.core.Either.Right -import arrow.core.raise.Raise +import arrow.core.raise.RaiseAccumulate import arrow.core.raise.fold import arrow.typeclasses.Monoid import arrow.typeclasses.Semigroup @@ -491,13 +491,13 @@ public fun Iterable.sequenceNullable(): List? = @OptIn(ExperimentalTypeInference::class) public inline fun Iterable.mapOrAccumulate( combine: (Error, Error) -> Error, - @BuilderInference transform: AccumulatingRaise.(A) -> B, + @BuilderInference transform: RaiseAccumulate.(A) -> B, ): Either> { var left: Any? = EmptyValue val right = ArrayList(collectionSizeOrDefault(10)) for (item in this) fold( - { transform(AccumulatingRaise(this), item) }, + { transform(RaiseAccumulate(this), item) }, { errors -> left = EmptyValue.combine(left, errors.reduce(combine), combine) }, { b -> right.add(b) } ) @@ -511,12 +511,12 @@ public inline fun Iterable.mapOrAccumulate( */ @OptIn(ExperimentalTypeInference::class) public inline fun Iterable.mapOrAccumulate( - @BuilderInference transform: AccumulatingRaise.(A) -> B, + @BuilderInference transform: RaiseAccumulate.(A) -> B, ): Either, List> { val left = mutableListOf() val right = ArrayList(collectionSizeOrDefault(10)) for (item in this) - fold({ transform(AccumulatingRaise(this), item) }, { error -> left.addAll(error) }, { b -> right.add(b) }) + fold({ transform(RaiseAccumulate(this), item) }, { error -> left.addAll(error) }, { b -> right.add(b) }) return left.toNonEmptyListOrNull()?.left() ?: right.right() } diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt index 7dce9dfe839..110d09a61cb 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt @@ -4,11 +4,9 @@ package arrow.core import arrow.core.Either.Left import arrow.core.Either.Right -import arrow.core.raise.Raise -import arrow.core.raise.either +import arrow.core.raise.RaiseAccumulate import arrow.typeclasses.Semigroup import kotlin.experimental.ExperimentalTypeInference -import kotlin.jvm.JvmName import kotlin.jvm.JvmStatic public typealias Nel = NonEmptyList @@ -492,12 +490,12 @@ public fun NonEmptyList>.sequence(semigroup: Semigroup public inline fun NonEmptyList.mapOrAccumulate( combine: (E, E) -> E, - @BuilderInference transform: AccumulatingRaise.(A) -> B + @BuilderInference transform: RaiseAccumulate.(A) -> B ): Either> = all.mapOrAccumulate(combine, transform).map { requireNotNull(it.toNonEmptyListOrNull()) } public inline fun NonEmptyList.mapOrAccumulate( - @BuilderInference transform: AccumulatingRaise.(A) -> B + @BuilderInference transform: RaiseAccumulate.(A) -> B ): Either, NonEmptyList> = all.mapOrAccumulate(transform).map { requireNotNull(it.toNonEmptyListOrNull()) } diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt index b344ab569df..6a1127682ec 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt @@ -4,13 +4,11 @@ package arrow.core import arrow.core.Either.Left import arrow.core.Either.Right -import arrow.core.raise.Raise -import arrow.core.raise.either +import arrow.core.raise.RaiseAccumulate import arrow.core.raise.fold import arrow.typeclasses.Monoid import arrow.typeclasses.Semigroup import kotlin.experimental.ExperimentalTypeInference -import kotlin.jvm.JvmName public fun Sequence.zip( c: Sequence, @@ -749,22 +747,22 @@ public fun Sequence.traverse( public fun Sequence.mapOrAccumulate( combine: (Error, Error) -> Error, - @BuilderInference transform: AccumulatingRaise.(A) -> B + @BuilderInference transform: RaiseAccumulate.(A) -> B ): Either> { var left: Any? = EmptyValue val right = mutableListOf() for (item in this) - fold({ transform(AccumulatingRaise(this), item) }, { errors -> left = EmptyValue.combine(left, errors.reduce(combine), combine) }, { b -> right.add(b) }) + fold({ transform(RaiseAccumulate(this), item) }, { errors -> left = EmptyValue.combine(left, errors.reduce(combine), combine) }, { b -> right.add(b) }) return if (left !== EmptyValue) EmptyValue.unbox(left).left() else right.right() } public fun Sequence.mapOrAccumulate( - @BuilderInference transform: AccumulatingRaise.(A) -> B + @BuilderInference transform: RaiseAccumulate.(A) -> B ): Either, List> { val left = mutableListOf() val right = mutableListOf() for (item in this) - fold({ transform(AccumulatingRaise(this), item) }, { errors -> left.addAll(errors) }, { b -> right.add(b) }) + fold({ transform(RaiseAccumulate(this), item) }, { errors -> left.addAll(errors) }, { b -> right.add(b) }) return left.toNonEmptyListOrNull()?.left() ?: right.right() } diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/map.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/map.kt index 279af414755..ed9d5ba3af2 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/map.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/map.kt @@ -8,7 +8,7 @@ import arrow.typeclasses.Monoid import arrow.typeclasses.Semigroup import kotlin.experimental.ExperimentalTypeInference import kotlin.collections.flatMap as _flatMap -import arrow.core.raise.Raise +import arrow.core.raise.RaiseAccumulate import arrow.core.raise.fold /** @@ -291,13 +291,13 @@ public inline fun Map.traverse( public inline fun Map.mapOrAccumulate( combine: (E, E) -> E, - @BuilderInference transform: AccumulatingRaise.(Map.Entry) -> B + @BuilderInference transform: RaiseAccumulate.(Map.Entry) -> B ): Either> { var left: Any? = EmptyValue val right = mutableMapOf() for (element in this) fold( - { transform(AccumulatingRaise(this), element) }, + { transform(RaiseAccumulate(this), element) }, { errors -> left = EmptyValue.combine(left, errors.reduce(combine), combine) }, { right[element.key] = it } ) @@ -305,12 +305,12 @@ public inline fun Map.mapOrAccumulate( } public inline fun Map.mapOrAccumulate( - @BuilderInference transform: AccumulatingRaise.(Map.Entry) -> B + @BuilderInference transform: RaiseAccumulate.(Map.Entry) -> B ): Either, Map> { val left = mutableListOf() val right = mutableMapOf() for (element in this) - fold({ transform(AccumulatingRaise(this), element) }, { error -> left.addAll(error) }, { right[element.key] = it }) + fold({ transform(RaiseAccumulate(this), element) }, { error -> left.addAll(error) }, { right[element.key] = it }) return left.toNonEmptyListOrNull()?.left() ?: right.right() } 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 431960c0d05..e1d31acc4c7 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 @@ -11,13 +11,17 @@ import arrow.core.Tuple5 import arrow.core.Tuple6 import arrow.core.Tuple7 import arrow.core.Tuple8 +import arrow.core.Validated import arrow.core.emptyCombine +import arrow.core.identity import arrow.core.nel +import arrow.core.nonEmptyListOf import arrow.typeclasses.Semigroup import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.experimental.ExperimentalTypeInference +import kotlin.js.JsName import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName @@ -568,3 +572,126 @@ public inline fun Raise>.mapOrAccumulate( @BuilderInference crossinline block: Raise.(A) -> B ): List = mapOrAccumulate(Semigroup.nonEmptyList(), list) { elt -> mapErrorNel { block(elt) } } + +/** + * This is a temporary solution for accumulating errors in the context of a [Raise], + * belonging to [Iterable.mapOrAccumulate], [NonEmptyList.mapOrAccumulate], [Sequence.mapOrAccumulate] & [Map.mapOrAccumulate]. + * + * All methods that are defined in [RaiseAccumulate] can be implemented using context receivers instead, + * this can be [DeprecationLevel.HIDDEN] in a source -and binary-compatible way when context receivers are released: + * + * ```kotlin + * context(Raise>) + * fun Either.bind(): A = when (this) { + * is Invalid -> raise(nonEmptyListOf(value)) + * is Valid -> value + * } + * + * @OptIn(ExperimentalTypeInference::class) + * public inline fun Iterable.mapOrAccumulate( + * @BuilderInference transform: Raise>.(A) -> B, + * ): Either, List> + * ``` + */ +public open class RaiseAccumulate( + private val raise: Raise> +): Raise> by raise { + + @RaiseDSL + @JsName("raise1") + @JvmName("raise1") + public fun raise(r: E): Nothing = + raise.raise(nonEmptyListOf(r)) + + /** + * Invoke an [EagerEffect] inside `this` [Raise] context. + * Any _logical failure_ is raised in `this` [Raise] context, + * and thus short-circuits the computation. + * + * @see [recover] if you want to attempt to recover from any _logical failure_. + */ + @JsName("invokeEager") + @JvmName("invokeEager") + public operator fun EagerEffect.invoke(): A = + fold({ raise(it) }, ::identity) + + @RaiseDSL + @JsName("bindEager") + @JvmName("bindEager") + public fun EagerEffect.bind(): A = + fold({ raise(it) }, ::identity) + + /** + * Invoke an [Effect] inside `this` [Raise] context. + * Any _logical failure_ raised are raised in `this` [Raise] context, + * and thus short-circuits the computation. + * + * @see [recover] if you want to attempt to recover from any _logical failure_. + */ + @JsName("invokeEffect") + @JvmName("invokeEffect") + public suspend operator fun Effect.invoke(): A = + fold({ raise(it) }, ::identity) + + @RaiseDSL + @JsName("bindEffect") + @JvmName("bindEffect") + public suspend fun Effect.bind(): A = + fold({ raise(it) }, ::identity) + + @RaiseDSL + @JsName("bindEither") + @JvmName("bindEither") + public fun Either.bind(): A = when (this) { + is Either.Left -> raise(value) + is Either.Right -> value + } + + /* Will be removed in subsequent PRs for Arrow 2.x.x */ + @RaiseDSL + @JsName("bindValidated") + @JvmName("bindValidated") + public fun Validated.bind(): A = when (this) { + is Validated.Invalid -> raise(value) + is Validated.Valid -> value + } + + @RaiseDSL + @JvmName("recoverEffect") + public suspend infix fun Effect.recover( + @BuilderInference resolve: suspend Raise>.(E) -> A + ): A = + fold({ this@recover.invoke(this) }, { throw it }, { resolve(it) }) { it } + + /** @see [recover] */ + @RaiseDSL + @JvmName("recoverEager") + public infix fun EagerEffect.recover(@BuilderInference resolve: Raise>.(E) -> A): A = + recover({ invoke() }) { resolve(it) } + + /** + * Execute the [Effect] resulting in [A], + * and recover from any _logical error_ of type [E], and [Throwable], by providing a fallback value of type [A], + * or raising a new error of type [R]. + * + * @see [catch] if you don't need to recover from [Throwable]. + */ + @RaiseDSL + @JvmName("recoverAndCatchEffect") + public suspend fun Effect.recover( + @BuilderInference recover: suspend Raise>.(E) -> A, + @BuilderInference catch: suspend Raise>.(Throwable) -> A, + ): A = fold({ invoke() }, { catch(it) }, { recover(it) }, { it }) + + @RaiseDSL + @JvmName("catchEffect") + public suspend infix fun Effect.catch( + @BuilderInference catch: suspend Raise>.(Throwable) -> A, + ): A = fold({ catch(it) }, { raise(it) }, { it }) + + @RaiseDSL + @JvmName("catchEagerEffect") + public infix fun EagerEffect.catch( + @BuilderInference catch: Raise>.(Throwable) -> A, + ): A = fold({ catch(it) }, { raise(it) }, { it }) +} diff --git a/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api b/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api index e77dd6bb784..dce1eff086e 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api +++ b/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api @@ -574,7 +574,7 @@ public final class arrow/fx/coroutines/ScheduleKt { public static final fun retryOrElseEither (Larrow/fx/coroutines/Schedule;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } -public final class arrow/fx/coroutines/ScopedAccumulatingRaise : arrow/core/AccumulatingRaise, kotlinx/coroutines/CoroutineScope { +public final class arrow/fx/coroutines/ScopedRaiseAccumulate : arrow/core/raise/RaiseAccumulate, kotlinx/coroutines/CoroutineScope { public fun (Larrow/core/raise/Raise;Lkotlinx/coroutines/CoroutineScope;)V public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext; } diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParMap.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParMap.kt index af4655e23d9..6b6b0b94dfc 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParMap.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParMap.kt @@ -1,6 +1,6 @@ package arrow.fx.coroutines -import arrow.core.AccumulatingRaise +import arrow.core.raise.RaiseAccumulate import arrow.core.Either import arrow.core.NonEmptyList import arrow.core.flattenOrAccumulate @@ -34,16 +34,16 @@ public suspend fun Iterable.parMap( } /** Temporary intersection type, until we have context receivers */ -public class ScopedAccumulatingRaise( +public class ScopedRaiseAccumulate( raise: Raise>, scope: CoroutineScope -) : CoroutineScope by scope, AccumulatingRaise(raise) +) : CoroutineScope by scope, RaiseAccumulate(raise) public suspend fun Iterable.parMapOrAccumulate( context: CoroutineContext = EmptyCoroutineContext, concurrency: Int, combine: (Error, Error) -> Error, - transform: suspend ScopedAccumulatingRaise.(A) -> B + transform: suspend ScopedRaiseAccumulate.(A) -> B ): Either> = coroutineScope { val semaphore = Semaphore(concurrency) @@ -51,7 +51,7 @@ public suspend fun Iterable.parMapOrAccumulate( async(context) { either { semaphore.withPermit { - transform(ScopedAccumulatingRaise(this, this@coroutineScope), it) + transform(ScopedRaiseAccumulate(this, this@coroutineScope), it) } } } @@ -61,13 +61,13 @@ public suspend fun Iterable.parMapOrAccumulate( public suspend fun Iterable.parMapOrAccumulate( context: CoroutineContext = EmptyCoroutineContext, combine: (Error, Error) -> Error, - transform: suspend ScopedAccumulatingRaise.(A) -> B + transform: suspend ScopedRaiseAccumulate.(A) -> B ): Either> = coroutineScope { map { async(context) { either { - transform(ScopedAccumulatingRaise(this, this@coroutineScope), it) + transform(ScopedRaiseAccumulate(this, this@coroutineScope), it) } } }.awaitAll().flattenOrAccumulate(combine) @@ -76,7 +76,7 @@ public suspend fun Iterable.parMapOrAccumulate( public suspend fun Iterable.parMapOrAccumulate( context: CoroutineContext = EmptyCoroutineContext, concurrency: Int, - transform: suspend ScopedAccumulatingRaise.(A) -> B + transform: suspend ScopedRaiseAccumulate.(A) -> B ): Either, List> = coroutineScope { val semaphore = Semaphore(concurrency) @@ -84,7 +84,7 @@ public suspend fun Iterable.parMapOrAccumulate( async(context) { either { semaphore.withPermit { - transform(ScopedAccumulatingRaise(this, this@coroutineScope), it) + transform(ScopedRaiseAccumulate(this, this@coroutineScope), it) } } } @@ -93,13 +93,13 @@ public suspend fun Iterable.parMapOrAccumulate( public suspend fun Iterable.parMapOrAccumulate( context: CoroutineContext = EmptyCoroutineContext, - transform: suspend ScopedAccumulatingRaise.(A) -> B + transform: suspend ScopedRaiseAccumulate.(A) -> B ): Either, List> = coroutineScope { map { async(context) { either { - transform(ScopedAccumulatingRaise(this, this@coroutineScope), it) + transform(ScopedRaiseAccumulate(this, this@coroutineScope), it) } } }.awaitAll().flattenOrAccumulate() From 2ace20feefe296f52e755f68f8d991a166f4f7ad Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Mon, 6 Mar 2023 12:05:04 +0100 Subject: [PATCH 09/19] Change RaiseAccumulate to Raise, and expose Nel based APIs --- arrow-libs/core/arrow-core/api/arrow-core.api | 23 ++-- .../commonMain/kotlin/arrow/core/Iterable.kt | 95 ++++++++++---- .../arrow/core/raise/RaiseAccumulate.kt | 119 +++--------------- .../kotlin/arrow/core/IterableTest.kt | 5 +- .../kotlin/examples/example-iterable-01.kt | 21 ++-- .../kotlin/examples/example-iterable-02.kt | 21 ++-- .../kotlin/examples/example-iterable-03.kt | 12 +- .../kotlin/examples/example-iterable-04.kt | 10 +- .../kotlin/examples/example-iterable-05.kt | 6 +- .../kotlin/examples/example-iterable-06.kt | 6 +- .../kotlin/examples/example-iterable-07.kt | 16 +-- .../kotlin/examples/example-iterable-08.kt | 14 ++- .../kotlin/examples/example-iterable-09.kt | 4 +- .../kotlin/examples/example-iterable-10.kt | 6 +- .../kotlin/examples/example-iterable-11.kt | 3 +- .../kotlin/examples/example-iterable-12.kt | 6 +- .../kotlin/examples/example-iterable-13.kt | 3 +- .../kotlin/examples/example-iterable-14.kt | 5 +- .../kotlin/examples/example-iterable-15.kt | 2 +- .../kotlin/examples/example-iterable-16.kt | 5 +- .../kotlin/examples/example-iterable-17.kt | 12 ++ .../kotlin/examples/example-iterable-18.kt | 14 +++ .../kotlin/examples/test/IterableKnitTest.kt | 17 +++ 23 files changed, 216 insertions(+), 209 deletions(-) create mode 100644 arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-17.kt create mode 100644 arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-18.kt create mode 100644 arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/test/IterableKnitTest.kt diff --git a/arrow-libs/core/arrow-core/api/arrow-core.api b/arrow-libs/core/arrow-core/api/arrow-core.api index eb2f90cec21..080633a0259 100644 --- a/arrow-libs/core/arrow-core/api/arrow-core.api +++ b/arrow-libs/core/arrow-core/api/arrow-core.api @@ -3414,30 +3414,21 @@ public class arrow/core/raise/RaiseAccumulate : arrow/core/raise/Raise { public fun bind (Larrow/core/continuations/Effect;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun bind (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public fun bind (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun bindEager (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; - public final fun bindEffect (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun bindEither (Larrow/core/Either;)Ljava/lang/Object; - public final fun bindValidated (Larrow/core/Validated;)Ljava/lang/Object; + public final fun bindNel (Larrow/core/Either;)Ljava/lang/Object; + public final fun bindNel (Larrow/core/Validated;)Ljava/lang/Object; public fun catch (Larrow/core/continuations/Effect;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun catch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public fun catch (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun catchEagerEffect (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; - public final fun catchEffect (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun getRaise ()Larrow/core/raise/Raise; public fun invoke (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public fun invoke (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun invokeEager (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; - public final fun invokeEffect (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun raise (Larrow/core/NonEmptyList;)Ljava/lang/Void; - public synthetic fun raise (Ljava/lang/Object;)Ljava/lang/Void; - public final fun raise1 (Ljava/lang/Object;)Ljava/lang/Void; + public final fun raise (Larrow/core/NonEmptyList;)Ljava/lang/Void; + 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/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun recover (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun recoverAndCatchEffect (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun recoverEager (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; - public final fun recoverEffect (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun shift (Larrow/core/NonEmptyList;)Ljava/lang/Object; - public synthetic fun shift (Ljava/lang/Object;)Ljava/lang/Object; + public fun shift (Ljava/lang/Object;)Ljava/lang/Object; + public final fun withNel (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; } public abstract interface annotation class arrow/core/raise/RaiseDSL : java/lang/annotation/Annotation { diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt index 3363c5524cb..ee3d7b73c38 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt @@ -1,9 +1,13 @@ @file:Suppress("unused", "FunctionName") +/** + * + */ package arrow.core import arrow.core.Either.Left import arrow.core.Either.Right +import arrow.core.raise.Raise import arrow.core.raise.RaiseAccumulate import arrow.core.raise.fold import arrow.typeclasses.Monoid @@ -387,7 +391,7 @@ public inline fun Iterable.traverseValidated(f: (A) -> ValidatedNel @OptIn(ExperimentalTypeInference::class) @OverloadResolutionByLambdaReturnType public inline fun Iterable.traverse(f: (A) -> ValidatedNel): ValidatedNel> = - mapOrAccumulate { f(it).bind() }.toValidated() + mapOrAccumulate { f(it).bindNel() }.toValidated() @Deprecated( "sequenceValidated is being renamed to sequence to simplify the Arrow API", @@ -421,7 +425,7 @@ public fun Iterable>.sequenceValidated(): ValidatedNel ) ) public fun Iterable>.sequence(): ValidatedNel> = - mapOrAccumulate, A> { it.bind() }.toValidated() + mapOrAccumulate { it.bindNel() }.toValidated() @Deprecated( "traverseOption is being renamed to traverse to simplify the Arrow API", @@ -483,10 +487,31 @@ public fun Iterable.sequenceNullable(): List? = sequence() /** - * Returns [Either] a [List] containing the results of applying the given [transform] function - * to each element in the original collection, - * **or** accumulate all the _logical errors_ that were _raised_ while transforming the collection. - * The [combine] function is used to accumulate all the _logical errors_. + * Returns [Either] a [List] containing the results of applying the given [transform] function to each element in the original collection, + * **or** accumulate all the _logical errors_ that were _raised_ while transforming the collection using the [combine] function is used to accumulate all the _logical errors_. + * + * Within this DSL you can `bind` both [Either], and [EitherNel] values and invoke [Raise] based function of _logical error_ type [Error]. Let's see an example of all the different cases: + * + * ```kotlin + * fun main() { + * listOf(1, 2, 3, 4).mapOrAccumulate({ a, b -> "$a, $b" }) { i -> + * when(i) { + * 1 -> "Either - $i".left().bind() + * 2 -> "Either - $i".leftNel().bindNel() + * 3 -> raise("Raise - $i") + * else -> withNel { raise(nonEmptyListOf("RaiseNel - $i")) } + * } + * } shouldBe "Either - 1, EitherNel - 2, Raise - 3, RaiseNel - 4".left() + * } + * ``` + * + * */ @OptIn(ExperimentalTypeInference::class) public inline fun Iterable.mapOrAccumulate( @@ -505,9 +530,31 @@ public inline fun Iterable.mapOrAccumulate( } /** - * Returns [Either] a [List] containing the results of applying the given [transform] function - * to each element in the original collection, + * Returns [Either] a [List] containing the results of applying the given [transform] function to each element in the original collection, * **or** accumulate all the _logical errors_ into a [NonEmptyList] that were _raised_ while applying the [transform] function. + * + * Let's see an example of all the different cases: + * + * ```kotlin + * fun main() { + * listOf(1, 2, 3, 4).mapOrAccumulate { i -> + * when(i) { + * 1 -> "Either - $i".left().bind() + * 2 -> "Either - $i".leftNel().bindNel() + * 3 -> raise("Raise - $i") + * else -> withNel { raise(nonEmptyListOf("RaiseNel - $i")) } + * } + * } shouldBe nonEmptyListOf("Either - 1", "EitherNel - 2", "Raise - 3", "RaiseNel - 4").left() + * } + * ``` + * + * */ @OptIn(ExperimentalTypeInference::class) public inline fun Iterable.mapOrAccumulate( @@ -624,7 +671,7 @@ public inline fun List.reduceRightNull( * println("noPadding = $noPadding") * } * ``` - * + * */ public fun Iterable.padZip(other: Iterable): List> = align(other) { ior -> @@ -655,7 +702,7 @@ public fun Iterable.padZip(other: Iterable): List> = * println("noPadding = $noPadding") * } * ``` - * + * */ public inline fun Iterable.padZip(other: Iterable, fa: (A?, B?) -> C): List = padZip(other).map { fa(it.first, it.second) } @@ -680,7 +727,7 @@ public inline fun Iterable.padZip(other: Iterable, fa: (A?, B?) * println("both = $both") * } * ``` - * + * */ public inline fun Iterable.leftPadZip(other: Iterable, fab: (A?, B) -> C): List = padZip(other) { a: A?, b: B? -> b?.let { fab(a, it) } }.mapNotNull(::identity) @@ -706,7 +753,7 @@ public inline fun Iterable.leftPadZip(other: Iterable, fab: (A?, * println("noPadding = $noPadding") * } * ``` - * + * */ public fun Iterable.leftPadZip(other: Iterable): List> = this.leftPadZip(other) { a, b -> a to b } @@ -731,7 +778,7 @@ public fun Iterable.leftPadZip(other: Iterable): List> * println("both = $both") * } * ``` - * + * */ public inline fun Iterable.rightPadZip(other: Iterable, fa: (A, B?) -> C): List = other.leftPadZip(this) { a, b -> fa(b, a) } @@ -756,7 +803,7 @@ public inline fun Iterable.rightPadZip(other: Iterable, fa: (A, * println("noPadding = $noPadding") * } * ``` - * + * */ public fun Iterable.rightPadZip(other: Iterable): List> = this.rightPadZip(other) { a, b -> a to b } @@ -777,7 +824,7 @@ public fun Iterable.rightPadZip(other: Iterable): List> * println(result) * } * ``` - * + * */ public inline fun Iterable.align(b: Iterable, fa: (Ior) -> C): List = buildList(maxOf(this.collectionSizeOrDefault(10), b.collectionSizeOrDefault(10))) { @@ -808,7 +855,7 @@ public inline fun Iterable.align(b: Iterable, fa: (Ior) -> * println(result) * } * ``` - * + * */ public fun Iterable.align(b: Iterable): List> = this.align(b, ::identity) @@ -841,7 +888,7 @@ public fun Iterable.salign( * println(result) * } * ``` - * + * */ public fun Iterable>.unzip(): Pair, List> = fold(emptyList() to emptyList()) { (l, r), x -> @@ -866,7 +913,7 @@ public fun Iterable>.unzip(): Pair, List> = * println(result) * } * ``` - * + * */ public inline fun Iterable.unzip(fc: (C) -> Pair): Pair, List> = map(fc).unzip() @@ -886,7 +933,7 @@ public inline fun Iterable.unzip(fc: (C) -> Pair): Pair + * */ public fun Iterable>.unalign(): Pair, List> = fold(emptyList() to emptyList()) { (l, r), x -> @@ -913,7 +960,7 @@ public fun Iterable>.unalign(): Pair, List> = * println(result) * } * ``` - * + * */ public inline fun Iterable.unalign(fa: (C) -> Ior): Pair, List> = map(fa).unalign() @@ -1061,7 +1108,7 @@ private tailrec fun Iterator.skip(count: Int): Iterator = * println(result) * } * ``` - * + * */ public fun Iterable.split(): Pair, A>? = firstOrNull()?.let { first -> @@ -1086,7 +1133,7 @@ public fun Iterable.tail(): List = * println(result) * } * ``` - * + * */ public fun Iterable.interleave(other: Iterable): List = this.split()?.let { (fa, a) -> @@ -1107,7 +1154,7 @@ public fun Iterable.interleave(other: Iterable): List = * println(result) * } * ``` - * + * */ public fun Iterable.unweave(ffa: (A) -> Iterable): List = split()?.let { (fa, a) -> @@ -1132,7 +1179,7 @@ public fun Iterable.unweave(ffa: (A) -> Iterable): List = * println(result) * } * ``` - * + * */ public inline fun Iterable.ifThen(fb: Iterable, ffa: (A) -> Iterable): Iterable = firstOrNull()?.let { first -> ffa(first) + tail().flatMap(ffa) } ?: fb.toList() 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 e1d31acc4c7..08a278c7cd4 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 @@ -3,7 +3,9 @@ @file:JvmName("RaiseKt") package arrow.core.raise +import arrow.core.mapOrAccumulate import arrow.core.Either +import arrow.core.EitherNel import arrow.core.EmptyValue import arrow.core.NonEmptyList import arrow.core.Tuple4 @@ -12,6 +14,7 @@ import arrow.core.Tuple6 import arrow.core.Tuple7 import arrow.core.Tuple8 import arrow.core.Validated +import arrow.core.ValidatedNel import arrow.core.emptyCombine import arrow.core.identity import arrow.core.nel @@ -574,124 +577,34 @@ public inline fun Raise>.mapOrAccumulate( mapOrAccumulate(Semigroup.nonEmptyList(), list) { elt -> mapErrorNel { block(elt) } } /** - * This is a temporary solution for accumulating errors in the context of a [Raise], - * belonging to [Iterable.mapOrAccumulate], [NonEmptyList.mapOrAccumulate], [Sequence.mapOrAccumulate] & [Map.mapOrAccumulate]. - * - * All methods that are defined in [RaiseAccumulate] can be implemented using context receivers instead, - * this can be [DeprecationLevel.HIDDEN] in a source -and binary-compatible way when context receivers are released: - * - * ```kotlin - * context(Raise>) - * fun Either.bind(): A = when (this) { - * is Invalid -> raise(nonEmptyListOf(value)) - * is Valid -> value - * } - * - * @OptIn(ExperimentalTypeInference::class) - * public inline fun Iterable.mapOrAccumulate( - * @BuilderInference transform: Raise>.(A) -> B, - * ): Either, List> - * ``` + * Receiver type belonging to [mapOrAccumulate]. + * Allows binding both [Either] and [EitherNel] values for [Either.Left] types of [Error]. + * It extends [Raise] of [Error], and allows working over [Raise] of [NonEmptyList] of [Error] as well. */ -public open class RaiseAccumulate( - private val raise: Raise> -): Raise> by raise { +public open class RaiseAccumulate( + public val raise: Raise> +): Raise { @RaiseDSL - @JsName("raise1") - @JvmName("raise1") - public fun raise(r: E): Nothing = + public override fun raise(r: Error): Nothing = raise.raise(nonEmptyListOf(r)) - /** - * Invoke an [EagerEffect] inside `this` [Raise] context. - * Any _logical failure_ is raised in `this` [Raise] context, - * and thus short-circuits the computation. - * - * @see [recover] if you want to attempt to recover from any _logical failure_. - */ - @JsName("invokeEager") - @JvmName("invokeEager") - public operator fun EagerEffect.invoke(): A = - fold({ raise(it) }, ::identity) - - @RaiseDSL - @JsName("bindEager") - @JvmName("bindEager") - public fun EagerEffect.bind(): A = - fold({ raise(it) }, ::identity) - - /** - * Invoke an [Effect] inside `this` [Raise] context. - * Any _logical failure_ raised are raised in `this` [Raise] context, - * and thus short-circuits the computation. - * - * @see [recover] if you want to attempt to recover from any _logical failure_. - */ - @JsName("invokeEffect") - @JvmName("invokeEffect") - public suspend operator fun Effect.invoke(): A = - fold({ raise(it) }, ::identity) - @RaiseDSL - @JsName("bindEffect") - @JvmName("bindEffect") - public suspend fun Effect.bind(): A = - fold({ raise(it) }, ::identity) + public fun raise(r: NonEmptyList): Nothing = + raise.raise(r) @RaiseDSL - @JsName("bindEither") - @JvmName("bindEither") - public fun Either.bind(): A = when (this) { + public fun EitherNel.bindNel(): A = when(this) { is Either.Left -> raise(value) is Either.Right -> value } - /* Will be removed in subsequent PRs for Arrow 2.x.x */ @RaiseDSL - @JsName("bindValidated") - @JvmName("bindValidated") - public fun Validated.bind(): A = when (this) { + public fun ValidatedNel.bindNel(): A = when(this) { is Validated.Invalid -> raise(value) is Validated.Valid -> value } - @RaiseDSL - @JvmName("recoverEffect") - public suspend infix fun Effect.recover( - @BuilderInference resolve: suspend Raise>.(E) -> A - ): A = - fold({ this@recover.invoke(this) }, { throw it }, { resolve(it) }) { it } - - /** @see [recover] */ - @RaiseDSL - @JvmName("recoverEager") - public infix fun EagerEffect.recover(@BuilderInference resolve: Raise>.(E) -> A): A = - recover({ invoke() }) { resolve(it) } - - /** - * Execute the [Effect] resulting in [A], - * and recover from any _logical error_ of type [E], and [Throwable], by providing a fallback value of type [A], - * or raising a new error of type [R]. - * - * @see [catch] if you don't need to recover from [Throwable]. - */ - @RaiseDSL - @JvmName("recoverAndCatchEffect") - public suspend fun Effect.recover( - @BuilderInference recover: suspend Raise>.(E) -> A, - @BuilderInference catch: suspend Raise>.(Throwable) -> A, - ): A = fold({ invoke() }, { catch(it) }, { recover(it) }, { it }) - - @RaiseDSL - @JvmName("catchEffect") - public suspend infix fun Effect.catch( - @BuilderInference catch: suspend Raise>.(Throwable) -> A, - ): A = fold({ catch(it) }, { raise(it) }, { it }) - - @RaiseDSL - @JvmName("catchEagerEffect") - public infix fun EagerEffect.catch( - @BuilderInference catch: Raise>.(Throwable) -> A, - ): A = fold({ catch(it) }, { raise(it) }, { it }) + public inline fun withNel(block: Raise>.() -> A): A = + block(raise) } diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/IterableTest.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/IterableTest.kt index 83e1c161984..c8f085283c7 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/IterableTest.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/IterableTest.kt @@ -55,8 +55,9 @@ class IterableTest : StringSpec({ "mapAccumulating accumulates" { checkAll(Arb.list(Arb.int())) { ints -> - val res: Either, List> = - ints.mapOrAccumulate { i -> if (i % 2 == 0) i else raise(i) } + val res = ints.mapOrAccumulate { i -> + if (i % 2 == 0) i else raise(i) + } val expected: Either, List> = ints.filterNot { it % 2 == 0 } .toNonEmptyListOrNull()?.left() ?: ints.filter { it % 2 == 0 }.right() diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-01.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-01.kt index e8e55884b74..7fbc17276f8 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-01.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-01.kt @@ -1,14 +1,19 @@ // This file was automatically generated from Iterable.kt by Knit tool. Do not edit. package arrow.core.examples.exampleIterable01 -import arrow.core.* - -val padRight = listOf(1, 2).padZip(listOf("a")) // Result: [Pair(1, "a"), Pair(2, null)] -val padLeft = listOf(1).padZip(listOf("a", "b")) // Result: [Pair(1, "a"), Pair(null, "b")] -val noPadding = listOf(1, 2).padZip(listOf("a", "b")) // Result: [Pair(1, "a"), Pair(2, "b")] +import arrow.core.left +import arrow.core.leftNel +import arrow.core.nonEmptyListOf +import arrow.core.mapOrAccumulate +import io.kotest.matchers.shouldBe fun main() { - println("padRight = $padRight") - println("padLeft = $padLeft") - println("noPadding = $noPadding") + listOf(1, 2, 3, 4).mapOrAccumulate({ a, b -> "$a, $b" }) { i -> + when(i) { + 1 -> "Either - $i".left().bind() + 2 -> "Either - $i".leftNel().bindNel() + 3 -> raise("Raise - $i") + else -> withNel { raise(nonEmptyListOf("RaiseNel - $i")) } + } + } shouldBe "Either - 1, EitherNel - 2, Raise - 3, RaiseNel - 4".left() } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-02.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-02.kt index de7a53a88a3..805f9050052 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-02.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-02.kt @@ -1,14 +1,19 @@ // This file was automatically generated from Iterable.kt by Knit tool. Do not edit. package arrow.core.examples.exampleIterable02 -import arrow.core.* - -val padZipRight = listOf(1, 2).padZip(listOf("a")) { l, r -> l to r } // Result: [Pair(1, "a"), Pair(2, null)] -val padZipLeft = listOf(1).padZip(listOf("a", "b")) { l, r -> l to r } // Result: [Pair(1, "a"), Pair(null, "b")] -val noPadding = listOf(1, 2).padZip(listOf("a", "b")) { l, r -> l to r } // Result: [Pair(1, "a"), Pair(2, "b")] +import arrow.core.left +import arrow.core.leftNel +import arrow.core.nonEmptyListOf +import arrow.core.mapOrAccumulate +import io.kotest.matchers.shouldBe fun main() { - println("padZipRight = $padZipRight") - println("padZipLeft = $padZipLeft") - println("noPadding = $noPadding") + listOf(1, 2, 3, 4).mapOrAccumulate { i -> + when(i) { + 1 -> "Either - $i".left().bind() + 2 -> "Either - $i".leftNel().bindNel() + 3 -> raise("Raise - $i") + else -> withNel { raise(nonEmptyListOf("RaiseNel - $i")) } + } + } shouldBe nonEmptyListOf("Either - 1", "EitherNel - 2", "Raise - 3", "RaiseNel - 4").left() } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-03.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-03.kt index f15d4349974..7220b01277e 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-03.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-03.kt @@ -3,12 +3,12 @@ package arrow.core.examples.exampleIterable03 import arrow.core.* -val left = listOf(1, 2).leftPadZip(listOf("a")) { l, r -> l to r } // Result: [Pair(1, "a")] -val right = listOf(1).leftPadZip(listOf("a", "b")) { l, r -> l to r } // Result: [Pair(1, "a"), Pair(null, "b")] -val both = listOf(1, 2).leftPadZip(listOf("a", "b")) { l, r -> l to r } // Result: [Pair(1, "a"), Pair(2, "b")] +val padRight = listOf(1, 2).padZip(listOf("a")) // Result: [Pair(1, "a"), Pair(2, null)] +val padLeft = listOf(1).padZip(listOf("a", "b")) // Result: [Pair(1, "a"), Pair(null, "b")] +val noPadding = listOf(1, 2).padZip(listOf("a", "b")) // Result: [Pair(1, "a"), Pair(2, "b")] fun main() { - println("left = $left") - println("right = $right") - println("both = $both") + println("padRight = $padRight") + println("padLeft = $padLeft") + println("noPadding = $noPadding") } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-04.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-04.kt index aa5492be95e..1a8bd707d50 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-04.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-04.kt @@ -3,12 +3,12 @@ package arrow.core.examples.exampleIterable04 import arrow.core.* -val padRight = listOf(1, 2).leftPadZip(listOf("a")) // Result: [Pair(1, "a")] -val padLeft = listOf(1).leftPadZip(listOf("a", "b")) // Result: [Pair(1, "a"), Pair(null, "b")] -val noPadding = listOf(1, 2).leftPadZip(listOf("a", "b")) // Result: [Pair(1, "a"), Pair(2, "b")] +val padZipRight = listOf(1, 2).padZip(listOf("a")) { l, r -> l to r } // Result: [Pair(1, "a"), Pair(2, null)] +val padZipLeft = listOf(1).padZip(listOf("a", "b")) { l, r -> l to r } // Result: [Pair(1, "a"), Pair(null, "b")] +val noPadding = listOf(1, 2).padZip(listOf("a", "b")) { l, r -> l to r } // Result: [Pair(1, "a"), Pair(2, "b")] fun main() { - println("padRight = $padRight") - println("padLeft = $padLeft") + println("padZipRight = $padZipRight") + println("padZipLeft = $padZipLeft") println("noPadding = $noPadding") } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-05.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-05.kt index 8ad499f39ff..27490224dde 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-05.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-05.kt @@ -3,9 +3,9 @@ package arrow.core.examples.exampleIterable05 import arrow.core.* -val left = listOf(1, 2).rightPadZip(listOf("a")) { l, r -> l to r } // Result: [Pair(1, "a"), Pair(null, "b")] -val right = listOf(1).rightPadZip(listOf("a", "b")) { l, r -> l to r } // Result: [Pair(1, "a")] -val both = listOf(1, 2).rightPadZip(listOf("a", "b")) { l, r -> l to r } // Result: [Pair(1, "a"), Pair(2, "b")] +val left = listOf(1, 2).leftPadZip(listOf("a")) { l, r -> l to r } // Result: [Pair(1, "a")] +val right = listOf(1).leftPadZip(listOf("a", "b")) { l, r -> l to r } // Result: [Pair(1, "a"), Pair(null, "b")] +val both = listOf(1, 2).leftPadZip(listOf("a", "b")) { l, r -> l to r } // Result: [Pair(1, "a"), Pair(2, "b")] fun main() { println("left = $left") diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-06.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-06.kt index 0c9feac4761..7e3c5822db4 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-06.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-06.kt @@ -3,9 +3,9 @@ package arrow.core.examples.exampleIterable06 import arrow.core.* -val padRight = listOf(1, 2).rightPadZip(listOf("a")) // Result: [Pair(1, "a"), Pair(2, null)] -val padLeft = listOf(1).rightPadZip(listOf("a", "b")) // Result: [Pair(1, "a")] -val noPadding = listOf(1, 2).rightPadZip(listOf("a", "b")) // Result: [Pair(1, "a"), Pair(2, "b")] +val padRight = listOf(1, 2).leftPadZip(listOf("a")) // Result: [Pair(1, "a")] +val padLeft = listOf(1).leftPadZip(listOf("a", "b")) // Result: [Pair(1, "a"), Pair(null, "b")] +val noPadding = listOf(1, 2).leftPadZip(listOf("a", "b")) // Result: [Pair(1, "a"), Pair(2, "b")] fun main() { println("padRight = $padRight") diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-07.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-07.kt index cefd2cecdbe..c17aa3f905b 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-07.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-07.kt @@ -3,12 +3,12 @@ package arrow.core.examples.exampleIterable07 import arrow.core.* -fun main(args: Array) { - //sampleStart - val result = - listOf("A", "B").align(listOf(1, 2, 3)) { - "$it" - } - //sampleEnd - println(result) +val left = listOf(1, 2).rightPadZip(listOf("a")) { l, r -> l to r } // Result: [Pair(1, "a"), Pair(null, "b")] +val right = listOf(1).rightPadZip(listOf("a", "b")) { l, r -> l to r } // Result: [Pair(1, "a")] +val both = listOf(1, 2).rightPadZip(listOf("a", "b")) { l, r -> l to r } // Result: [Pair(1, "a"), Pair(2, "b")] + +fun main() { + println("left = $left") + println("right = $right") + println("both = $both") } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-08.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-08.kt index ecd6d9d8d12..2d0bc39d9f2 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-08.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-08.kt @@ -3,10 +3,12 @@ package arrow.core.examples.exampleIterable08 import arrow.core.* -fun main(args: Array) { - //sampleStart - val result = - listOf("A", "B").align(listOf(1, 2, 3)) - //sampleEnd - println(result) +val padRight = listOf(1, 2).rightPadZip(listOf("a")) // Result: [Pair(1, "a"), Pair(2, null)] +val padLeft = listOf(1).rightPadZip(listOf("a", "b")) // Result: [Pair(1, "a")] +val noPadding = listOf(1, 2).rightPadZip(listOf("a", "b")) // Result: [Pair(1, "a"), Pair(2, "b")] + +fun main() { + println("padRight = $padRight") + println("padLeft = $padLeft") + println("noPadding = $noPadding") } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-09.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-09.kt index 77a24907c74..171ca9eef8b 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-09.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-09.kt @@ -6,7 +6,9 @@ import arrow.core.* fun main(args: Array) { //sampleStart val result = - listOf("A" to 1, "B" to 2).unzip() + listOf("A", "B").align(listOf(1, 2, 3)) { + "$it" + } //sampleEnd println(result) } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-10.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-10.kt index 18947bafeec..5c8535c4b19 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-10.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-10.kt @@ -6,11 +6,7 @@ import arrow.core.* fun main(args: Array) { //sampleStart val result = - listOf("A:1", "B:2", "C:3").unzip { e -> - e.split(":").let { - it.first() to it.last() - } - } + listOf("A", "B").align(listOf(1, 2, 3)) //sampleEnd println(result) } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-11.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-11.kt index 02cd4700e7b..fa2f025d97b 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-11.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-11.kt @@ -6,8 +6,7 @@ import arrow.core.* fun main(args: Array) { //sampleStart val result = - listOf(("A" to 1).bothIor(), ("B" to 2).bothIor(), "C".leftIor()) - .unalign() + listOf("A" to 1, "B" to 2).unzip() //sampleEnd println(result) } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-12.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-12.kt index b26baaa2191..28758c5065c 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-12.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-12.kt @@ -6,9 +6,11 @@ import arrow.core.* fun main(args: Array) { //sampleStart val result = - listOf(1, 2, 3).unalign { - it.leftIor() + listOf("A:1", "B:2", "C:3").unzip { e -> + e.split(":").let { + it.first() to it.last() } + } //sampleEnd println(result) } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-13.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-13.kt index aba3d491dba..f93dd99df72 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-13.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-13.kt @@ -6,7 +6,8 @@ import arrow.core.* fun main(args: Array) { //sampleStart val result = - listOf("A", "B", "C").split() + listOf(("A" to 1).bothIor(), ("B" to 2).bothIor(), "C".leftIor()) + .unalign() //sampleEnd println(result) } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-14.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-14.kt index 1d230b0e4e8..c2143c1bc65 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-14.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-14.kt @@ -5,9 +5,10 @@ import arrow.core.* fun main(args: Array) { //sampleStart - val tags = List(10) { "#" } val result = - tags.interleave(listOf("A", "B", "C")) + listOf(1, 2, 3).unalign { + it.leftIor() + } //sampleEnd println(result) } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-15.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-15.kt index 3b4f078741e..e915d242aca 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-15.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-15.kt @@ -6,7 +6,7 @@ import arrow.core.* fun main(args: Array) { //sampleStart val result = - listOf(1,2,3).unweave { i -> listOf("$i, ${i + 1}") } + listOf("A", "B", "C").split() //sampleEnd println(result) } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-16.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-16.kt index 5868fe6d58d..7b2b976d4a1 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-16.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-16.kt @@ -5,10 +5,9 @@ import arrow.core.* fun main(args: Array) { //sampleStart + val tags = List(10) { "#" } val result = - listOf(1,2,3).ifThen(listOf("empty")) { i -> - listOf("$i, ${i + 1}") - } + tags.interleave(listOf("A", "B", "C")) //sampleEnd println(result) } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-17.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-17.kt new file mode 100644 index 00000000000..8d4b009b66d --- /dev/null +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-17.kt @@ -0,0 +1,12 @@ +// This file was automatically generated from Iterable.kt by Knit tool. Do not edit. +package arrow.core.examples.exampleIterable17 + +import arrow.core.* + +fun main(args: Array) { + //sampleStart + val result = + listOf(1,2,3).unweave { i -> listOf("$i, ${i + 1}") } + //sampleEnd + println(result) +} diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-18.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-18.kt new file mode 100644 index 00000000000..b127d9abb1b --- /dev/null +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-18.kt @@ -0,0 +1,14 @@ +// This file was automatically generated from Iterable.kt by Knit tool. Do not edit. +package arrow.core.examples.exampleIterable18 + +import arrow.core.* + +fun main(args: Array) { + //sampleStart + val result = + listOf(1,2,3).ifThen(listOf("empty")) { i -> + listOf("$i, ${i + 1}") + } + //sampleEnd + println(result) +} diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/test/IterableKnitTest.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/test/IterableKnitTest.kt new file mode 100644 index 00000000000..debed4ee2b7 --- /dev/null +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/test/IterableKnitTest.kt @@ -0,0 +1,17 @@ +// This file was automatically generated from Iterable.kt by Knit tool. Do not edit. +package arrow.core.examples.test + +import io.kotest.core.spec.style.StringSpec + +class IterableKnitTest : StringSpec({ + "ExampleIterable01" { + arrow.core.examples.exampleIterable01.test() + } + + "ExampleIterable02" { + arrow.core.examples.exampleIterable02.test() + } + +}) { + override fun timeout(): Long = 1000 +} From fa14515ef782743304229eb397c53a456dbbf6db Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Mon, 6 Mar 2023 14:08:37 +0100 Subject: [PATCH 10/19] Fix knit --- .../arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt | 4 ++-- .../src/jvmTest/kotlin/examples/example-iterable-01.kt | 2 +- .../src/jvmTest/kotlin/examples/example-iterable-02.kt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt index ee3d7b73c38..fdb7f661c39 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt @@ -499,7 +499,7 @@ public fun Iterable.sequenceNullable(): List? = * import io.kotest.matchers.shouldBe * --> * ```kotlin - * fun main() { + * fun test() { * listOf(1, 2, 3, 4).mapOrAccumulate({ a, b -> "$a, $b" }) { i -> * when(i) { * 1 -> "Either - $i".left().bind() @@ -542,7 +542,7 @@ public inline fun Iterable.mapOrAccumulate( * import io.kotest.matchers.shouldBe * --> * ```kotlin - * fun main() { + * fun test() { * listOf(1, 2, 3, 4).mapOrAccumulate { i -> * when(i) { * 1 -> "Either - $i".left().bind() diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-01.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-01.kt index 7fbc17276f8..04291adc54c 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-01.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-01.kt @@ -7,7 +7,7 @@ import arrow.core.nonEmptyListOf import arrow.core.mapOrAccumulate import io.kotest.matchers.shouldBe -fun main() { +fun test() { listOf(1, 2, 3, 4).mapOrAccumulate({ a, b -> "$a, $b" }) { i -> when(i) { 1 -> "Either - $i".left().bind() diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-02.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-02.kt index 805f9050052..2d6e22d239d 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-02.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-02.kt @@ -7,7 +7,7 @@ import arrow.core.nonEmptyListOf import arrow.core.mapOrAccumulate import io.kotest.matchers.shouldBe -fun main() { +fun test() { listOf(1, 2, 3, 4).mapOrAccumulate { i -> when(i) { 1 -> "Either - $i".left().bind() From acab94ce163e28f8dd3b39fa65dfba248c136c9f Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Mon, 6 Mar 2023 14:50:31 +0100 Subject: [PATCH 11/19] Fix knit test --- .../arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt | 4 ++-- .../src/jvmTest/kotlin/examples/example-iterable-01.kt | 2 +- .../src/jvmTest/kotlin/examples/example-iterable-02.kt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt index fdb7f661c39..816a3e41728 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt @@ -503,7 +503,7 @@ public fun Iterable.sequenceNullable(): List? = * listOf(1, 2, 3, 4).mapOrAccumulate({ a, b -> "$a, $b" }) { i -> * when(i) { * 1 -> "Either - $i".left().bind() - * 2 -> "Either - $i".leftNel().bindNel() + * 2 -> "EitherNel - $i".leftNel().bindNel() * 3 -> raise("Raise - $i") * else -> withNel { raise(nonEmptyListOf("RaiseNel - $i")) } * } @@ -546,7 +546,7 @@ public inline fun Iterable.mapOrAccumulate( * listOf(1, 2, 3, 4).mapOrAccumulate { i -> * when(i) { * 1 -> "Either - $i".left().bind() - * 2 -> "Either - $i".leftNel().bindNel() + * 2 -> "EitherNel - $i".leftNel().bindNel() * 3 -> raise("Raise - $i") * else -> withNel { raise(nonEmptyListOf("RaiseNel - $i")) } * } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-01.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-01.kt index 04291adc54c..ec7c5b7cd2a 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-01.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-01.kt @@ -11,7 +11,7 @@ fun test() { listOf(1, 2, 3, 4).mapOrAccumulate({ a, b -> "$a, $b" }) { i -> when(i) { 1 -> "Either - $i".left().bind() - 2 -> "Either - $i".leftNel().bindNel() + 2 -> "EitherNel - $i".leftNel().bindNel() 3 -> raise("Raise - $i") else -> withNel { raise(nonEmptyListOf("RaiseNel - $i")) } } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-02.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-02.kt index 2d6e22d239d..6158cc8a53a 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-02.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-iterable-02.kt @@ -11,7 +11,7 @@ fun test() { listOf(1, 2, 3, 4).mapOrAccumulate { i -> when(i) { 1 -> "Either - $i".left().bind() - 2 -> "Either - $i".leftNel().bindNel() + 2 -> "EitherNel - $i".leftNel().bindNel() 3 -> raise("Raise - $i") else -> withNel { raise(nonEmptyListOf("RaiseNel - $i")) } } From 851fa2c615da9ea9fcbccf761ea3cde2d8973b10 Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Mon, 6 Mar 2023 14:50:31 +0100 Subject: [PATCH 12/19] Move related changes here --- arrow-libs/core/arrow-core/api/arrow-core.api | 1 - .../commonMain/kotlin/arrow/core/Iterable.kt | 134 +++++------------- .../arrow/core/raise/RaiseAccumulate.kt | 8 +- 3 files changed, 40 insertions(+), 103 deletions(-) diff --git a/arrow-libs/core/arrow-core/api/arrow-core.api b/arrow-libs/core/arrow-core/api/arrow-core.api index 080633a0259..9ece638d6dd 100644 --- a/arrow-libs/core/arrow-core/api/arrow-core.api +++ b/arrow-libs/core/arrow-core/api/arrow-core.api @@ -3422,7 +3422,6 @@ public class arrow/core/raise/RaiseAccumulate : arrow/core/raise/Raise { public final fun getRaise ()Larrow/core/raise/Raise; public fun invoke (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public fun invoke (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun raise (Larrow/core/NonEmptyList;)Ljava/lang/Void; 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/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt index 816a3e41728..c62347e4af2 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt @@ -290,10 +290,7 @@ public inline fun Iterable.zip( internal fun Iterable.collectionSizeOrDefault(default: Int): Int = if (this is Collection<*>) this.size else default -@Deprecated( - "traverseEither is being renamed to traverse to simplify the Arrow API", - ReplaceWith("traverse(f)", "arrow.core.traverse") -) +@Deprecated("traverseEither is being renamed to traverse to simplify the Arrow API", ReplaceWith("traverse(f)", "arrow.core.traverse")) public inline fun Iterable.traverseEither(f: (A) -> Either): Either> = traverse(f) @@ -310,10 +307,7 @@ public inline fun Iterable.traverse(f: (A) -> Either): Either return destination.right() } -@Deprecated( - "sequenceEither is being renamed to sequence to simplify the Arrow API", - ReplaceWith("sequence()", "arrow.core.sequence") -) +@Deprecated("sequenceEither is being renamed to sequence to simplify the Arrow API", ReplaceWith("sequence()", "arrow.core.sequence")) public fun Iterable>.sequenceEither(): Either> = traverse(::identity) @@ -332,27 +326,18 @@ public inline fun Iterable.traverse(f: (A) -> Result): Result Iterable.traverseResult(f: (A) -> Result): Result> = traverse(f) -@Deprecated( - "sequenceResult is being renamed to sequence to simplify the Arrow API", - ReplaceWith("sequence()", "arrow.core.sequence") -) +@Deprecated("sequenceResult is being renamed to sequence to simplify the Arrow API", ReplaceWith("sequence()", "arrow.core.sequence")) public fun Iterable>.sequenceResult(): Result> = sequence() public fun Iterable>.sequence(): Result> = traverse(::identity) -@Deprecated( - "traverseValidated is being renamed to traverse to simplify the Arrow API", - ReplaceWith("traverse(semigroup, f)", "arrow.core.traverse") -) +@Deprecated("traverseValidated is being renamed to traverse to simplify the Arrow API", ReplaceWith("traverse(semigroup, f)", "arrow.core.traverse")) public inline fun Iterable.traverseValidated( semigroup: Semigroup, f: (A) -> Validated @@ -374,17 +359,14 @@ public inline fun Iterable.traverse( ): Validated> = mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { f(it).bind() }.toValidated() -@Deprecated( - "traverseValidated is being renamed to traverse to simplify the Arrow API", - ReplaceWith("traverse(f)", "arrow.core.traverse") -) +@Deprecated("traverseValidated is being renamed to traverse to simplify the Arrow API", ReplaceWith("traverse(f)", "arrow.core.traverse")) public inline fun Iterable.traverseValidated(f: (A) -> ValidatedNel): ValidatedNel> = traverse(f) @Deprecated( ValidatedDeprMsg + "Use the mapOrAccumulate API instead", ReplaceWith( - "mapOrAccumulate { f(it).bind() }.toValidated()", + "mapOrAccumulate { f(it).bindNel() }.toValidated()", "arrow.core.mapOrAccumulate" ) ) @@ -393,10 +375,7 @@ public inline fun Iterable.traverseValidated(f: (A) -> ValidatedNel public inline fun Iterable.traverse(f: (A) -> ValidatedNel): ValidatedNel> = mapOrAccumulate { f(it).bindNel() }.toValidated() -@Deprecated( - "sequenceValidated is being renamed to sequence to simplify the Arrow API", - ReplaceWith("sequence(semigroup)", "arrow.core.sequence") -) +@Deprecated("sequenceValidated is being renamed to sequence to simplify the Arrow API", ReplaceWith("sequence(semigroup)", "arrow.core.sequence")) public fun Iterable>.sequenceValidated(semigroup: Semigroup): Validated> = sequence(semigroup) @@ -410,27 +389,21 @@ public fun Iterable>.sequenceValidated(semigroup: Semigro public fun Iterable>.sequence(semigroup: Semigroup): Validated> = mapOrAccumulate({ a, b -> semigroup.run { a.combine(b) } }) { it.bind() }.toValidated() -@Deprecated( - "sequenceValidated is being renamed to sequence to simplify the Arrow API", - ReplaceWith("sequence()", "arrow.core.sequence") -) +@Deprecated("sequenceValidated is being renamed to sequence to simplify the Arrow API", ReplaceWith("sequence()", "arrow.core.sequence")) public fun Iterable>.sequenceValidated(): ValidatedNel> = sequence() @Deprecated( ValidatedDeprMsg + "Use the mapOrAccumulate API instead", ReplaceWith( - "mapOrAccumulate, A> { it.bind() }.toValidated()", + "mapOrAccumulate, A> { it.bindNel() }.toValidated()", "arrow.core.mapOrAccumulate" ) ) public fun Iterable>.sequence(): ValidatedNel> = mapOrAccumulate { it.bindNel() }.toValidated() -@Deprecated( - "traverseOption is being renamed to traverse to simplify the Arrow API", - ReplaceWith("traverse(f)", "arrow.core.traverse") -) +@Deprecated("traverseOption is being renamed to traverse to simplify the Arrow API", ReplaceWith("traverse(f)", "arrow.core.traverse")) public inline fun Iterable.traverseOption(f: (A) -> Option): Option> = traverse(f) @@ -447,20 +420,14 @@ public inline fun Iterable.traverse(f: (A) -> Option): Option Iterable>.sequenceOption(): Option> = sequence() public fun Iterable>.sequence(): Option> = traverse(::identity) -@Deprecated( - "traverseNullable is being renamed to traverse to simplify the Arrow API", - ReplaceWith("traverse(f)", "arrow.core.traverse") -) +@Deprecated("traverseNullable is being renamed to traverse to simplify the Arrow API", ReplaceWith("traverse(f)", "arrow.core.traverse")) public inline fun Iterable.traverseNullable(f: (A) -> B?): List? = traverse(f) @@ -479,10 +446,7 @@ public inline fun Iterable.traverse(f: (A) -> B?): List? { return acc.toList() } -@Deprecated( - "sequenceNullable is being renamed to sequence to simplify the Arrow API", - ReplaceWith("sequence()", "arrow.core.sequence") -) +@Deprecated("sequenceNullable is being renamed to sequence to simplify the Arrow API", ReplaceWith("sequence()", "arrow.core.sequence")) public fun Iterable.sequenceNullable(): List? = sequence() @@ -568,60 +532,38 @@ public inline fun Iterable.mapOrAccumulate( } /** - * Flatten a list of [Either] into a single [Either] with a list of values, or accumulates all errors using [combine]. + * Flatten an [Iterable] of [Either]. + * Alias for [mapOrAccumulate] over an [Iterable] of computed [Either]. + * Either returns a [List] containing all [Either.Right] values, or [Either.Left] values accumulated using [combine]. */ -public inline fun Iterable>.flattenOrAccumulate(combine: (Error, Error) -> Error): Either> { - var left: Any? = EmptyValue - val right = ArrayList(collectionSizeOrDefault(10)) - for (item in this) { - when (item) { - is Right -> right.add(item.value) - is Left -> left = EmptyValue.combine(left, item.value, combine) - } - } - return if (left !== EmptyValue) EmptyValue.unbox(left).left() else right.right() -} +public inline fun Iterable>.flattenOrAccumulate(combine: (Error, Error) -> Error): Either> = + mapOrAccumulate(combine) { it.bind() } +/** + * Flatten an [Iterable] of [Either]. + * Alias for [mapOrAccumulate] over an [Iterable] of computed [Either]. + * Either returns a [List] containing all [Either.Right] values, or [EitherNel] [Left] values accumulated using [combine]. + */ @JvmName("flattenNelOrAccumulate") -public fun Iterable>.flattenOrAccumulate(combine: (Error, Error) -> Error): Either> { - var left: Any? = EmptyValue - val right = ArrayList(collectionSizeOrDefault(10)) - for (item in this) { - when (item) { - is Right -> right.add(item.value) - is Left -> left = EmptyValue.combine(left, item.value.reduce(combine), combine) - } - } - return if (left !== EmptyValue) EmptyValue.unbox(left).left() else right.right() -} +public fun Iterable>.flattenOrAccumulate(combine: (Error, Error) -> Error): Either> = + mapOrAccumulate(combine) { it.bindNel() } /** - * Flatten a list of [Either] into a single [Either] with a list of values, or accumulates all errors with into an [NonEmptyList]. + * Flatten an [Iterable] of [Either]. + * Alias for [mapOrAccumulate] over an [Iterable] of computed [Either]. + * Either returns a [List] containing all [Either.Right] values, or a [NonEmptyList] of all [Either.Left] values. */ -public fun Iterable>.flattenOrAccumulate(): Either, List> { - val left = mutableListOf() - val right = ArrayList(collectionSizeOrDefault(10)) - for (item in this) { - when (item) { - is Right -> right.add(item.value) - is Left -> left.add(item.value) - } - } - return left.toNonEmptyListOrNull()?.left() ?: right.right() -} +public fun Iterable>.flattenOrAccumulate(): Either, List> = + mapOrAccumulate { it.bind() } +/** + * Flatten an [Iterable] of [Either]. + * Alias for [mapOrAccumulate] over an [Iterable] of computed [Either]. + * Either returns a [List] containing all [Either.Right] values, or a [NonEmptyList] of all [EitherNel] [Left] values. + */ @JvmName("flattenNelOrAccumulate") -public fun Iterable>.flattenOrAccumulate(): Either, List> { - val left = mutableListOf() - val right = ArrayList(collectionSizeOrDefault(10)) - for (item in this) { - when (item) { - is Right -> right.add(item.value) - is Left -> left.addAll(item.value) - } - } - return left.toNonEmptyListOrNull()?.left() ?: right.right() -} +public fun Iterable>.flattenOrAccumulate(): Either, List> = + mapOrAccumulate { it.bindNel() } public fun Iterable.sequence(): List? = traverse(::identity) 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 08a278c7cd4..f62f0700531 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 @@ -589,19 +589,15 @@ public open class RaiseAccumulate( public override fun raise(r: Error): Nothing = raise.raise(nonEmptyListOf(r)) - @RaiseDSL - public fun raise(r: NonEmptyList): Nothing = - raise.raise(r) - @RaiseDSL public fun EitherNel.bindNel(): A = when(this) { - is Either.Left -> raise(value) + is Either.Left -> raise.raise(value) is Either.Right -> value } @RaiseDSL public fun ValidatedNel.bindNel(): A = when(this) { - is Validated.Invalid -> raise(value) + is Validated.Invalid -> raise.raise(value) is Validated.Valid -> value } From 9c88c4f7c24d7a54568069884b3e83c62f99ce36 Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Mon, 6 Mar 2023 13:55:29 +0100 Subject: [PATCH 13/19] Update Raise.xOrAccumulate --- arrow-libs/core/arrow-core/api/arrow-core.api | 35 +- .../commonMain/kotlin/arrow/core/Iterable.kt | 22 +- .../arrow/core/raise/RaiseAccumulate.kt | 754 ++++++++---------- 3 files changed, 335 insertions(+), 476 deletions(-) diff --git a/arrow-libs/core/arrow-core/api/arrow-core.api b/arrow-libs/core/arrow-core/api/arrow-core.api index 9ece638d6dd..838a55a597a 100644 --- a/arrow-libs/core/arrow-core/api/arrow-core.api +++ b/arrow-libs/core/arrow-core/api/arrow-core.api @@ -3456,9 +3456,8 @@ public final class arrow/core/raise/RaiseKt { public static final fun getOrNull (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public static final fun getOrNull (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun ior (Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function1;)Larrow/core/Ior; - public static final fun mapErrorNel (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; - public static final fun mapOrAccumulate (Larrow/core/raise/Raise;Larrow/typeclasses/Semigroup;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;)Ljava/util/List; public static final fun mapOrAccumulate (Larrow/core/raise/Raise;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;)Ljava/util/List; + public static final fun mapOrAccumulate (Larrow/core/raise/Raise;Ljava/lang/Iterable;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)Ljava/util/List; public static final fun merge (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public static final fun merge (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun nullable (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; @@ -3483,22 +3482,22 @@ public final class arrow/core/raise/RaiseKt { public static final fun toResult (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun toValidated (Lkotlin/jvm/functions/Function1;)Larrow/core/Validated; public static final fun toValidated (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function10;)Ljava/lang/Object; - public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function9;)Ljava/lang/Object; - public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function8;)Ljava/lang/Object; - public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function7;)Ljava/lang/Object; - public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function6;)Ljava/lang/Object; - public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;)Ljava/lang/Object; - public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function4;)Ljava/lang/Object; - public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;)Ljava/lang/Object; - public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function10;)Ljava/lang/Object; - public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function9;)Ljava/lang/Object; - public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function8;)Ljava/lang/Object; - public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function7;)Ljava/lang/Object; - public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function6;)Ljava/lang/Object; - public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;)Ljava/lang/Object; - public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function4;)Ljava/lang/Object; - public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;)Ljava/lang/Object; + public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function9;)Ljava/lang/Object; + public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function8;)Ljava/lang/Object; + public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function7;)Ljava/lang/Object; + public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function6;)Ljava/lang/Object; + public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;)Ljava/lang/Object; + public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function4;)Ljava/lang/Object; + public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;)Ljava/lang/Object; + public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function9;)Ljava/lang/Object; + public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function8;)Ljava/lang/Object; + public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function7;)Ljava/lang/Object; + public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function6;)Ljava/lang/Object; + public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;)Ljava/lang/Object; + public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function4;)Ljava/lang/Object; + public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;)Ljava/lang/Object; + public static final fun zipOrAccumulate (Larrow/core/raise/Raise;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; } public final class arrow/core/raise/ResultRaise : arrow/core/raise/Raise { diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt index c62347e4af2..e44b03055c5 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt @@ -9,7 +9,9 @@ import arrow.core.Either.Left import arrow.core.Either.Right import arrow.core.raise.Raise import arrow.core.raise.RaiseAccumulate +import arrow.core.raise.either import arrow.core.raise.fold +import arrow.core.raise.mapOrAccumulate import arrow.typeclasses.Monoid import arrow.typeclasses.Semigroup import kotlin.Result.Companion.success @@ -481,16 +483,8 @@ public fun Iterable.sequenceNullable(): List? = public inline fun Iterable.mapOrAccumulate( combine: (Error, Error) -> Error, @BuilderInference transform: RaiseAccumulate.(A) -> B, -): Either> { - var left: Any? = EmptyValue - val right = ArrayList(collectionSizeOrDefault(10)) - for (item in this) - fold( - { transform(RaiseAccumulate(this), item) }, - { errors -> left = EmptyValue.combine(left, errors.reduce(combine), combine) }, - { b -> right.add(b) } - ) - return if (left !== EmptyValue) EmptyValue.unbox(left).left() else right.right() +): Either> = either { + mapOrAccumulate(this@mapOrAccumulate, combine, transform) } /** @@ -523,12 +517,8 @@ public inline fun Iterable.mapOrAccumulate( @OptIn(ExperimentalTypeInference::class) public inline fun Iterable.mapOrAccumulate( @BuilderInference transform: RaiseAccumulate.(A) -> B, -): Either, List> { - val left = mutableListOf() - val right = ArrayList(collectionSizeOrDefault(10)) - for (item in this) - fold({ transform(RaiseAccumulate(this), item) }, { error -> left.addAll(error) }, { b -> right.add(b) }) - return left.toNonEmptyListOrNull()?.left() ?: right.right() +): Either, List> = either { + mapOrAccumulate(this@mapOrAccumulate, transform) } /** 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 f62f0700531..a67937c9fc2 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 @@ -1,4 +1,4 @@ -@file:OptIn(ExperimentalTypeInference::class, ExperimentalContracts::class) +@file:OptIn(ExperimentalTypeInference::class) @file:JvmMultifileClass @file:JvmName("RaiseKt") package arrow.core.raise @@ -7,280 +7,216 @@ import arrow.core.mapOrAccumulate import arrow.core.Either import arrow.core.EitherNel import arrow.core.EmptyValue +import arrow.core.EmptyValue.combine +import arrow.core.EmptyValue.unbox import arrow.core.NonEmptyList -import arrow.core.Tuple4 -import arrow.core.Tuple5 -import arrow.core.Tuple6 -import arrow.core.Tuple7 -import arrow.core.Tuple8 import arrow.core.Validated +import arrow.core.collectionSizeOrDefault import arrow.core.ValidatedNel -import arrow.core.emptyCombine -import arrow.core.identity -import arrow.core.nel import arrow.core.nonEmptyListOf -import arrow.typeclasses.Semigroup -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract +import arrow.core.toNonEmptyListOrNull import kotlin.experimental.ExperimentalTypeInference -import kotlin.js.JsName import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName /** - * Accumulate the errors from running both [action1] and [action2] - * using the given [semigroup]. + * Accumulate the errors from running both [action1] and [action2] using the given [combine] function. */ @RaiseDSL public inline fun Raise.zipOrAccumulate( - semigroup: Semigroup<@UnsafeVariance R>, - @BuilderInference action1: Raise.() -> A, - @BuilderInference action2: Raise.() -> B, - @BuilderInference block: Raise.(A, B) -> C -): C { - contract { - callsInPlace(action1, InvocationKind.EXACTLY_ONCE) - callsInPlace(action2, InvocationKind.EXACTLY_ONCE) + combine: (R, R) -> R, + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference block: (A, B) -> C +): C = + zipOrAccumulate( + combine, + action1, + action2, + { }) { a, b, _ -> + block(a, b) } - val result1 = either(action1) - val result2 = either(action2) - return when (result1) { - is Either.Right -> - when (result2) { - is Either.Right -> block(result1.value, result2.value) - is Either.Left -> raise(result2.value) - } - is Either.Left -> - when (result2) { - is Either.Right -> raise(result1.value) - is Either.Left -> raise(semigroup.run { result1.value + result2.value }) - } - } -} /** - * Accumulate the errors from running [action1], [action2], and [action3] - * using the given [semigroup]. + * Accumulate the errors from running [action1], [action2], and [action3] using the given [combine]. */ @RaiseDSL public inline fun Raise.zipOrAccumulate( - semigroup: Semigroup<@UnsafeVariance R>, - @BuilderInference action1: Raise.() -> A, - @BuilderInference action2: Raise.() -> B, - @BuilderInference action3: Raise.() -> C, - @BuilderInference block: Raise.(A, B, C) -> D -): D { - contract { - callsInPlace(action1, InvocationKind.EXACTLY_ONCE) - callsInPlace(action2, InvocationKind.EXACTLY_ONCE) - callsInPlace(action3, InvocationKind.EXACTLY_ONCE) + combine: (R, R) -> R, + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference block: (A, B, C) -> D +): D = + zipOrAccumulate( + combine, + action1, + action2, + action3, + { }) { a, b, c, _ -> + block(a, b, c) } - return zipOrAccumulate( - semigroup, - { zipOrAccumulate(semigroup, action1, action2) { x, y -> x to y } }, - action3 - ) { xy, z -> block(xy.first, xy.second, z) } -} /** - * Accumulate the errors from running [action1], [action2], [action3], and [action4] - * using the given [semigroup]. + * Accumulate the errors from running [action1], [action2], [action3], and [action4] using the given [combine]. */ @RaiseDSL public inline fun Raise.zipOrAccumulate( - semigroup: Semigroup<@UnsafeVariance R>, - @BuilderInference action1: Raise.() -> A, - @BuilderInference action2: Raise.() -> B, - @BuilderInference action3: Raise.() -> C, - @BuilderInference action4: Raise.() -> D, - @BuilderInference block: Raise.(A, B, C, D) -> E -): E { - contract { - callsInPlace(action1, InvocationKind.EXACTLY_ONCE) - callsInPlace(action2, InvocationKind.EXACTLY_ONCE) - callsInPlace(action3, InvocationKind.EXACTLY_ONCE) - callsInPlace(action4, InvocationKind.EXACTLY_ONCE) + combine: (R, R) -> R, + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference block: (A, B, C, D) -> E +): E = + zipOrAccumulate( + combine, + action1, + action2, + action3, + action4, + { }) { a, b, c, d, _ -> + block(a, b, c, d) } - return zipOrAccumulate( - semigroup, - { zipOrAccumulate(semigroup, action1, action2, action3) { x, y, z -> Triple(x, y, z) } }, - action4 - ) { xyz, z -> block(xyz.first, xyz.second, xyz.third, z) } -} /** - * Accumulate the errors from running [action1], [action2], [action3], [action4], and [action5] - * using the given [semigroup]. + * Accumulate the errors from running [action1], [action2], [action3], [action4], and [action5] using the given [combine]. */ @RaiseDSL public inline fun Raise.zipOrAccumulate( - semigroup: Semigroup<@UnsafeVariance R>, - @BuilderInference action1: Raise.() -> A, - @BuilderInference action2: Raise.() -> B, - @BuilderInference action3: Raise.() -> C, - @BuilderInference action4: Raise.() -> D, - @BuilderInference action5: Raise.() -> E, - @BuilderInference block: Raise.(A, B, C, D, E) -> F -): F { - contract { - callsInPlace(action1, InvocationKind.EXACTLY_ONCE) - callsInPlace(action2, InvocationKind.EXACTLY_ONCE) - callsInPlace(action3, InvocationKind.EXACTLY_ONCE) - callsInPlace(action4, InvocationKind.EXACTLY_ONCE) - callsInPlace(action5, InvocationKind.EXACTLY_ONCE) + combine: (R, R) -> R, + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + @BuilderInference block: (A, B, C, D, E) -> F +): F = + zipOrAccumulate( + combine, + action1, + action2, + action3, + action4, + action5, + { }) { a, b, c, d, e, _ -> + block(a, b, c, d, e) } - return zipOrAccumulate( - semigroup, - { zipOrAccumulate(semigroup, action1, action2, action3, action4) { x, y, z, u -> Tuple4(x, y, z, u) } }, - action5 - ) { xyzu, v -> block(xyzu.first, xyzu.second, xyzu.third, xyzu.fourth, v) } -} /** - * Accumulate the errors from running [action1], [action2], [action3], [action4], [action5], and [action6] - * using the given [semigroup]. + * Accumulate the errors from running [action1], [action2], [action3], [action4], [action5], and [action6] using the given [combine]. */ @RaiseDSL public inline fun Raise.zipOrAccumulate( - semigroup: Semigroup<@UnsafeVariance R>, - @BuilderInference action1: Raise.() -> A, - @BuilderInference action2: Raise.() -> B, - @BuilderInference action3: Raise.() -> C, - @BuilderInference action4: Raise.() -> D, - @BuilderInference action5: Raise.() -> E, - @BuilderInference action6: Raise.() -> F, - @BuilderInference block: Raise.(A, B, C, D, E, F) -> G -): G { - contract { - callsInPlace(action1, InvocationKind.EXACTLY_ONCE) - callsInPlace(action2, InvocationKind.EXACTLY_ONCE) - callsInPlace(action3, InvocationKind.EXACTLY_ONCE) - callsInPlace(action4, InvocationKind.EXACTLY_ONCE) - callsInPlace(action5, InvocationKind.EXACTLY_ONCE) - callsInPlace(action6, InvocationKind.EXACTLY_ONCE) + combine: (R, R) -> R, + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + @BuilderInference action6: RaiseAccumulate.() -> F, + @BuilderInference block: (A, B, C, D, E, F) -> G +): G = + zipOrAccumulate( + combine, + action1, + action2, + action3, + action4, + action5, + action6, + { }) { a, b, c, d, e, f, _ -> + block(a, b, c, d, e, f) } - return zipOrAccumulate( - semigroup, - { zipOrAccumulate(semigroup, action1, action2, action3, action4, action5) { x, y, z, u, v -> Tuple5(x, y, z, u, v) } }, - action6 - ) { xyzuv, w -> block(xyzuv.first, xyzuv.second, xyzuv.third, xyzuv.fourth, xyzuv.fifth, w) } -} /** - * Accumulate the errors from running [action1], [action2], [action3], [action4], [action5], [action6], and [action7] - * using the given [semigroup]. + * Accumulate the errors from running [action1], [action2], [action3], [action4], [action5], [action6], and [action7] using the given [combine]. */ @RaiseDSL public inline fun Raise.zipOrAccumulate( - semigroup: Semigroup<@UnsafeVariance R>, - @BuilderInference action1: Raise.() -> A, - @BuilderInference action2: Raise.() -> B, - @BuilderInference action3: Raise.() -> C, - @BuilderInference action4: Raise.() -> D, - @BuilderInference action5: Raise.() -> E, - @BuilderInference action6: Raise.() -> F, - @BuilderInference action7: Raise.() -> G, - @BuilderInference block: Raise.(A, B, C, D, E, F, G) -> H -): H { - contract { - callsInPlace(action1, InvocationKind.EXACTLY_ONCE) - callsInPlace(action2, InvocationKind.EXACTLY_ONCE) - callsInPlace(action3, InvocationKind.EXACTLY_ONCE) - callsInPlace(action4, InvocationKind.EXACTLY_ONCE) - callsInPlace(action5, InvocationKind.EXACTLY_ONCE) - callsInPlace(action6, InvocationKind.EXACTLY_ONCE) - callsInPlace(action7, InvocationKind.EXACTLY_ONCE) + combine: (R, R) -> R, + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + @BuilderInference action6: RaiseAccumulate.() -> F, + @BuilderInference action7: RaiseAccumulate.() -> G, + @BuilderInference block: (A, B, C, D, E, F, G) -> H +): H = + zipOrAccumulate( + combine, + action1, + action2, + action3, + action4, + action5, + action6, + action7, + { }) { a, b, c, d, e, f, g, _ -> + block(a, b, c, d, e, f, g) } - return zipOrAccumulate( - semigroup, - { zipOrAccumulate(semigroup, action1, action2, action3, action4, action5, action6) { x, y, z, u, v, w -> Tuple6(x, y, z, u, v, w) } }, - action7 - ) { xyzuvw, a -> block(xyzuvw.first, xyzuvw.second, xyzuvw.third, xyzuvw.fourth, xyzuvw.fifth, xyzuvw.sixth, a) } -} /** - * Accumulate the errors from running [action1], [action2], [action3], [action4], [action5], [action6], [action7], and [action8] - * using the given [semigroup]. + * Accumulate the errors from running [action1], [action2], [action3], [action4], [action5], [action6], [action7], and [action8] using the given [combine]. */ @RaiseDSL public inline fun Raise.zipOrAccumulate( - semigroup: Semigroup<@UnsafeVariance R>, - @BuilderInference action1: Raise.() -> A, - @BuilderInference action2: Raise.() -> B, - @BuilderInference action3: Raise.() -> C, - @BuilderInference action4: Raise.() -> D, - @BuilderInference action5: Raise.() -> E, - @BuilderInference action6: Raise.() -> F, - @BuilderInference action7: Raise.() -> G, - @BuilderInference action8: Raise.() -> H, - @BuilderInference block: Raise.(A, B, C, D, E, F, G, H) -> I -): I { - contract { - callsInPlace(action1, InvocationKind.EXACTLY_ONCE) - callsInPlace(action2, InvocationKind.EXACTLY_ONCE) - callsInPlace(action3, InvocationKind.EXACTLY_ONCE) - callsInPlace(action4, InvocationKind.EXACTLY_ONCE) - callsInPlace(action5, InvocationKind.EXACTLY_ONCE) - callsInPlace(action6, InvocationKind.EXACTLY_ONCE) - callsInPlace(action7, InvocationKind.EXACTLY_ONCE) - callsInPlace(action8, InvocationKind.EXACTLY_ONCE) + combine: (R, R) -> R, + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + @BuilderInference action6: RaiseAccumulate.() -> F, + @BuilderInference action7: RaiseAccumulate.() -> G, + @BuilderInference action8: RaiseAccumulate.() -> H, + @BuilderInference block: (A, B, C, D, E, F, G, H) -> I +): I = + zipOrAccumulate( + combine, + action1, + action2, + action3, + action4, + action5, + action6, + action7, + action8, + { }) { a, b, c, d, e, f, g, h, _ -> + block(a, b, c, d, e, f, g, h) } - return zipOrAccumulate( - semigroup, - { zipOrAccumulate(semigroup, action1, action2, action3, action4, action5, action6, action7) { x, y, z, u, v, w, a -> Tuple7(x, y, z, u, v, w, a) } }, - action8 - ) { xyzuvwa, b -> block(xyzuvwa.first, xyzuvwa.second, xyzuvwa.third, xyzuvwa.fourth, xyzuvwa.fifth, xyzuvwa.sixth, xyzuvwa.seventh, b) } -} /** - * Accumulate the errors from running [action1], [action2], [action3], [action4], [action5], [action6], [action7], [action8], and [action9] - * using the given [semigroup]. + * Accumulate the errors from running [action1], [action2], [action3], [action4], [action5], [action6], [action7], [action8], and [action9] using the given [combine]. */ @RaiseDSL public inline fun Raise.zipOrAccumulate( - semigroup: Semigroup<@UnsafeVariance R>, - @BuilderInference action1: Raise.() -> A, - @BuilderInference action2: Raise.() -> B, - @BuilderInference action3: Raise.() -> C, - @BuilderInference action4: Raise.() -> D, - @BuilderInference action5: Raise.() -> E, - @BuilderInference action6: Raise.() -> F, - @BuilderInference action7: Raise.() -> G, - @BuilderInference action8: Raise.() -> H, - @BuilderInference action9: Raise.() -> I, - @BuilderInference block: Raise.(A, B, C, D, E, F, G, H, I) -> J + combine: (R, R) -> R, + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + @BuilderInference action6: RaiseAccumulate.() -> F, + @BuilderInference action7: RaiseAccumulate.() -> G, + @BuilderInference action8: RaiseAccumulate.() -> H, + @BuilderInference action9: RaiseAccumulate.() -> I, + @BuilderInference block: (A, B, C, D, E, F, G, H, I) -> J ): J { - contract { - callsInPlace(action1, InvocationKind.EXACTLY_ONCE) - callsInPlace(action2, InvocationKind.EXACTLY_ONCE) - callsInPlace(action3, InvocationKind.EXACTLY_ONCE) - callsInPlace(action4, InvocationKind.EXACTLY_ONCE) - callsInPlace(action5, InvocationKind.EXACTLY_ONCE) - callsInPlace(action6, InvocationKind.EXACTLY_ONCE) - callsInPlace(action7, InvocationKind.EXACTLY_ONCE) - callsInPlace(action8, InvocationKind.EXACTLY_ONCE) - callsInPlace(action9, InvocationKind.EXACTLY_ONCE) - } - return zipOrAccumulate( - semigroup, - { zipOrAccumulate(semigroup, action1, action2, action3, action4, action5, action6, action7, action8) { x, y, z, u, v, w, a, b -> Tuple8(x, y, z, u, v, w, a, b) } }, - action9 - ) { xyzuvwab, c -> block(xyzuvwab.first, xyzuvwab.second, xyzuvwab.third, xyzuvwab.fourth, xyzuvwab.fifth, xyzuvwab.sixth, xyzuvwab.seventh, xyzuvwab.eighth, c) } -} - -/** - * Re-raise any errors in [block] in a [NonEmptyList]. - */ -@RaiseDSL -public inline fun Raise>.mapErrorNel( - crossinline block: Raise.() -> A -): A { - contract { - callsInPlace(block, InvocationKind.EXACTLY_ONCE) - } - return recover(block) { raise(it.nel()) } + var error: Any? = EmptyValue + val a = recover({ action1(RaiseAccumulate(this)) }) { error = combine(error, it.reduce(combine), combine); EmptyValue } + val b = recover({ action2(RaiseAccumulate(this)) }) { error = combine(error, it.reduce(combine), combine); EmptyValue } + val c = recover({ action3(RaiseAccumulate(this)) }) { error = combine(error, it.reduce(combine), combine); EmptyValue } + val d = recover({ action4(RaiseAccumulate(this)) }) { error = combine(error, it.reduce(combine), combine); EmptyValue } + val e = recover({ action5(RaiseAccumulate(this)) }) { error = combine(error, it.reduce(combine), combine); EmptyValue } + val f = recover({ action6(RaiseAccumulate(this)) }) { error = combine(error, it.reduce(combine), combine); EmptyValue } + val g = recover({ action7(RaiseAccumulate(this)) }) { error = combine(error, it.reduce(combine), combine); EmptyValue } + val h = recover({ action8(RaiseAccumulate(this)) }) { error = combine(error, it.reduce(combine), combine); EmptyValue } + val i = recover({ action9(RaiseAccumulate(this)) }) { error = combine(error, it.reduce(combine), combine); EmptyValue } + return if (error !== EmptyValue) raise(unbox(error)) + else block(unbox(a), unbox(b), unbox(c), unbox(d), unbox(e), unbox(f), unbox(g), unbox(h), unbox(i)) } /** @@ -288,293 +224,226 @@ public inline fun Raise>.mapErrorNel( */ @RaiseDSL public inline fun Raise>.zipOrAccumulate( - @BuilderInference crossinline action1: Raise.() -> A, - @BuilderInference crossinline action2: Raise.() -> B, - @BuilderInference crossinline block: Raise.(A, B) -> C -): C { - contract { - callsInPlace(action1, InvocationKind.EXACTLY_ONCE) - callsInPlace(action2, InvocationKind.EXACTLY_ONCE) + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + block: (A, B) -> C +): C = + zipOrAccumulate( + action1, + action2, + {}) { a, b, _ -> + block(a, b) } - return zipOrAccumulate( - Semigroup.nonEmptyList(), - { mapErrorNel(action1) }, - { mapErrorNel(action2) }, - { x, y -> mapErrorNel { block(x, y) } } - ) -} /** * Accumulate the errors from running [action1], [action2], and [action3]. */ @RaiseDSL public inline fun Raise>.zipOrAccumulate( - @BuilderInference crossinline action1: Raise.() -> A, - @BuilderInference crossinline action2: Raise.() -> B, - @BuilderInference crossinline action3: Raise.() -> C, - @BuilderInference crossinline block: Raise.(A, B, C) -> D -): D { - contract { - callsInPlace(action1, InvocationKind.EXACTLY_ONCE) - callsInPlace(action2, InvocationKind.EXACTLY_ONCE) - callsInPlace(action3, InvocationKind.EXACTLY_ONCE) + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference block: (A, B, C) -> D +): D = + zipOrAccumulate( + action1, + action2, + action3, + {}) { a, b, c, _ -> + block(a, b, c) } - return zipOrAccumulate( - Semigroup.nonEmptyList(), - { mapErrorNel(action1) }, - { mapErrorNel(action2) }, - { mapErrorNel(action3) }, - { x, y, z -> mapErrorNel { block(x, y, z) } } - ) -} /** * Accumulate the errors from running [action1], [action2], [action3], and [action4]. */ @RaiseDSL public inline fun Raise>.zipOrAccumulate( - @BuilderInference crossinline action1: Raise.() -> A, - @BuilderInference crossinline action2: Raise.() -> B, - @BuilderInference crossinline action3: Raise.() -> C, - @BuilderInference crossinline action4: Raise.() -> D, - @BuilderInference crossinline block: Raise.(A, B, C, D) -> E -): E { - contract { - callsInPlace(action1, InvocationKind.EXACTLY_ONCE) - callsInPlace(action2, InvocationKind.EXACTLY_ONCE) - callsInPlace(action3, InvocationKind.EXACTLY_ONCE) - callsInPlace(action4, InvocationKind.EXACTLY_ONCE) + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference block: (A, B, C, D) -> E +): E = + zipOrAccumulate( + action1, + action2, + action3, + action4, + {}) { a, b, c, d, _ -> + block(a, b, c, d) } - return zipOrAccumulate( - Semigroup.nonEmptyList(), - { mapErrorNel(action1) }, - { mapErrorNel(action2) }, - { mapErrorNel(action3) }, - { mapErrorNel(action4) }, - { x, y, z, u -> mapErrorNel { block(x, y, z, u) } } - ) -} /** * Accumulate the errors from running [action1], [action2], [action3], [action4], and [action5]. */ @RaiseDSL public inline fun Raise>.zipOrAccumulate( - @BuilderInference crossinline action1: Raise.() -> A, - @BuilderInference crossinline action2: Raise.() -> B, - @BuilderInference crossinline action3: Raise.() -> C, - @BuilderInference crossinline action4: Raise.() -> D, - @BuilderInference crossinline action5: Raise.() -> E, - @BuilderInference crossinline block: Raise.(A, B, C, D, E) -> F -): F { - contract { - callsInPlace(action1, InvocationKind.EXACTLY_ONCE) - callsInPlace(action2, InvocationKind.EXACTLY_ONCE) - callsInPlace(action3, InvocationKind.EXACTLY_ONCE) - callsInPlace(action4, InvocationKind.EXACTLY_ONCE) - callsInPlace(action5, InvocationKind.EXACTLY_ONCE) + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + @BuilderInference block: (A, B, C, D, E) -> F +): F = + zipOrAccumulate( + action1, + action2, + action3, + action4, + action5, + {}) { a, b, c, d, e, _ -> + block(a, b, c, d, e) } - return zipOrAccumulate( - Semigroup.nonEmptyList(), - { mapErrorNel(action1) }, - { mapErrorNel(action2) }, - { mapErrorNel(action3) }, - { mapErrorNel(action4) }, - { mapErrorNel(action5) }, - { x, y, z, u, v -> mapErrorNel { block(x, y, z, u, v) } } - ) -} /** * Accumulate the errors from running [action1], [action2], [action3], [action4], [action5], and [action6]. */ @RaiseDSL public inline fun Raise>.zipOrAccumulate( - @BuilderInference crossinline action1: Raise.() -> A, - @BuilderInference crossinline action2: Raise.() -> B, - @BuilderInference crossinline action3: Raise.() -> C, - @BuilderInference crossinline action4: Raise.() -> D, - @BuilderInference crossinline action5: Raise.() -> E, - @BuilderInference crossinline action6: Raise.() -> F, - @BuilderInference crossinline block: Raise.(A, B, C, D, E, F) -> G -): G { - contract { - callsInPlace(action1, InvocationKind.EXACTLY_ONCE) - callsInPlace(action2, InvocationKind.EXACTLY_ONCE) - callsInPlace(action3, InvocationKind.EXACTLY_ONCE) - callsInPlace(action4, InvocationKind.EXACTLY_ONCE) - callsInPlace(action5, InvocationKind.EXACTLY_ONCE) - callsInPlace(action6, InvocationKind.EXACTLY_ONCE) + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + @BuilderInference action6: RaiseAccumulate.() -> F, + @BuilderInference block: (A, B, C, D, E, F) -> G +): G = + zipOrAccumulate( + action1, + action2, + action3, + action4, + action5, + action6, + {}) { a, b, c, d, e, f, _ -> + block(a, b, c, d, e, f) } - return zipOrAccumulate( - Semigroup.nonEmptyList(), - { mapErrorNel(action1) }, - { mapErrorNel(action2) }, - { mapErrorNel(action3) }, - { mapErrorNel(action4) }, - { mapErrorNel(action5) }, - { mapErrorNel(action6) }, - { x, y, z, u, v, w -> mapErrorNel { block(x, y, z, u, v, w) } } - ) -} /** * Accumulate the errors from running [action1], [action2], [action3], [action4], [action5], [action6], and [action7]. */ @RaiseDSL public inline fun Raise>.zipOrAccumulate( - @BuilderInference crossinline action1: Raise.() -> A, - @BuilderInference crossinline action2: Raise.() -> B, - @BuilderInference crossinline action3: Raise.() -> C, - @BuilderInference crossinline action4: Raise.() -> D, - @BuilderInference crossinline action5: Raise.() -> E, - @BuilderInference crossinline action6: Raise.() -> F, - @BuilderInference crossinline action7: Raise.() -> G, - @BuilderInference crossinline block: Raise.(A, B, C, D, E, F, G) -> H -): H { - contract { - callsInPlace(action1, InvocationKind.EXACTLY_ONCE) - callsInPlace(action2, InvocationKind.EXACTLY_ONCE) - callsInPlace(action3, InvocationKind.EXACTLY_ONCE) - callsInPlace(action4, InvocationKind.EXACTLY_ONCE) - callsInPlace(action5, InvocationKind.EXACTLY_ONCE) - callsInPlace(action6, InvocationKind.EXACTLY_ONCE) - callsInPlace(action7, InvocationKind.EXACTLY_ONCE) + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + @BuilderInference action6: RaiseAccumulate.() -> F, + @BuilderInference action7: RaiseAccumulate.() -> G, + @BuilderInference block: (A, B, C, D, E, F, G) -> H +): H = + zipOrAccumulate( + action1, + action2, + action3, + action4, + action5, + action6, + action7, + {}) { a, b, c, d, e, f, g, _ -> + block(a, b, c, d, e, f, g) } - return zipOrAccumulate( - Semigroup.nonEmptyList(), - { mapErrorNel(action1) }, - { mapErrorNel(action2) }, - { mapErrorNel(action3) }, - { mapErrorNel(action4) }, - { mapErrorNel(action5) }, - { mapErrorNel(action6) }, - { mapErrorNel(action7) }, - { x, y, z, u, v, w, a -> mapErrorNel { block(x, y, z, u, v, w, a) } } - ) -} /** * Accumulate the errors from running [action1], [action2], [action3], [action4], [action5], [action6], [action7], and [action8]. */ @RaiseDSL public inline fun Raise>.zipOrAccumulate( - @BuilderInference crossinline action1: Raise.() -> A, - @BuilderInference crossinline action2: Raise.() -> B, - @BuilderInference crossinline action3: Raise.() -> C, - @BuilderInference crossinline action4: Raise.() -> D, - @BuilderInference crossinline action5: Raise.() -> E, - @BuilderInference crossinline action6: Raise.() -> F, - @BuilderInference crossinline action7: Raise.() -> G, - @BuilderInference crossinline action8: Raise.() -> H, - @BuilderInference crossinline block: Raise.(A, B, C, D, E, F, G, H) -> I -): I { - contract { - callsInPlace(action1, InvocationKind.EXACTLY_ONCE) - callsInPlace(action2, InvocationKind.EXACTLY_ONCE) - callsInPlace(action3, InvocationKind.EXACTLY_ONCE) - callsInPlace(action4, InvocationKind.EXACTLY_ONCE) - callsInPlace(action5, InvocationKind.EXACTLY_ONCE) - callsInPlace(action6, InvocationKind.EXACTLY_ONCE) - callsInPlace(action7, InvocationKind.EXACTLY_ONCE) - callsInPlace(action8, InvocationKind.EXACTLY_ONCE) + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + @BuilderInference action6: RaiseAccumulate.() -> F, + @BuilderInference action7: RaiseAccumulate.() -> G, + @BuilderInference action8: RaiseAccumulate.() -> H, + @BuilderInference block: (A, B, C, D, E, F, G, H) -> I +): I = + zipOrAccumulate( + action1, + action2, + action3, + action4, + action5, + action6, + action7, + action8, + {}) { a, b, c, d, e, f, g, h, _ -> + block(a, b, c, d, e, f, g, h) } - return zipOrAccumulate( - Semigroup.nonEmptyList(), - { mapErrorNel(action1) }, - { mapErrorNel(action2) }, - { mapErrorNel(action3) }, - { mapErrorNel(action4) }, - { mapErrorNel(action5) }, - { mapErrorNel(action6) }, - { mapErrorNel(action7) }, - { mapErrorNel(action8) }, - { x, y, z, u, v, w, a, b -> mapErrorNel { block(x, y, z, u, v, w, a, b) } } - ) -} /** * Accumulate the errors from running [action1], [action2], [action3], [action4], [action5], [action6], [action7], [action8], and [action9]. */ @RaiseDSL public inline fun Raise>.zipOrAccumulate( - @BuilderInference crossinline action1: Raise.() -> A, - @BuilderInference crossinline action2: Raise.() -> B, - @BuilderInference crossinline action3: Raise.() -> C, - @BuilderInference crossinline action4: Raise.() -> D, - @BuilderInference crossinline action5: Raise.() -> E, - @BuilderInference crossinline action6: Raise.() -> F, - @BuilderInference crossinline action7: Raise.() -> G, - @BuilderInference crossinline action8: Raise.() -> H, - @BuilderInference crossinline action9: Raise.() -> I, - @BuilderInference crossinline block: Raise.(A, B, C, D, E, F, G, H, I) -> J + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + @BuilderInference action6: RaiseAccumulate.() -> F, + @BuilderInference action7: RaiseAccumulate.() -> G, + @BuilderInference action8: RaiseAccumulate.() -> H, + @BuilderInference action9: RaiseAccumulate.() -> I, + @BuilderInference block: (A, B, C, D, E, F, G, H, I) -> J ): J { - contract { - callsInPlace(action1, InvocationKind.EXACTLY_ONCE) - callsInPlace(action2, InvocationKind.EXACTLY_ONCE) - callsInPlace(action3, InvocationKind.EXACTLY_ONCE) - callsInPlace(action4, InvocationKind.EXACTLY_ONCE) - callsInPlace(action5, InvocationKind.EXACTLY_ONCE) - callsInPlace(action6, InvocationKind.EXACTLY_ONCE) - callsInPlace(action7, InvocationKind.EXACTLY_ONCE) - callsInPlace(action8, InvocationKind.EXACTLY_ONCE) - callsInPlace(action9, InvocationKind.EXACTLY_ONCE) - } - return zipOrAccumulate( - Semigroup.nonEmptyList(), - { mapErrorNel(action1) }, - { mapErrorNel(action2) }, - { mapErrorNel(action3) }, - { mapErrorNel(action4) }, - { mapErrorNel(action5) }, - { mapErrorNel(action6) }, - { mapErrorNel(action7) }, - { mapErrorNel(action8) }, - { mapErrorNel(action9) }, - { x, y, z, u, v, w, a, b, c -> mapErrorNel { block(x, y, z, u, v, w, a, b, c) } } - ) + val error: MutableList = mutableListOf() + val a = recover({ action1(RaiseAccumulate(this)) }) { error.addAll(it); EmptyValue } + val b = recover({ action2(RaiseAccumulate(this)) }) { error.addAll(it); EmptyValue } + val c = recover({ action3(RaiseAccumulate(this)) }) { error.addAll(it); EmptyValue } + val d = recover({ action4(RaiseAccumulate(this)) }) { error.addAll(it); EmptyValue } + val e = recover({ action5(RaiseAccumulate(this)) }) { error.addAll(it); EmptyValue } + val f = recover({ action6(RaiseAccumulate(this)) }) { error.addAll(it); EmptyValue } + val g = recover({ action7(RaiseAccumulate(this)) }) { error.addAll(it); EmptyValue } + val h = recover({ action8(RaiseAccumulate(this)) }) { error.addAll(it); EmptyValue } + val i = recover({ action9(RaiseAccumulate(this)) }) { error.addAll(it); EmptyValue } + error.toNonEmptyListOrNull()?.let { raise(it) } + return block(unbox(a), unbox(b), unbox(c), unbox(d), unbox(e), unbox(f), unbox(g), unbox(h), unbox(i)) } /** - * Accumulate the errors obtained by executing the [block] - * over every element of [list] using the given [semigroup]. + * Transform every element of [list] using the given [transform], or accumulate all the occurred errors using [combine]. */ @RaiseDSL public inline fun Raise.mapOrAccumulate( - semigroup: Semigroup<@UnsafeVariance R>, list: Iterable, - @BuilderInference block: Raise.(A) -> B + combine: (R, R) -> R, + @BuilderInference transform: RaiseAccumulate.(A) -> B ): List { - // this could be implemented using [zipOrAccumulate], - // but we can have a faster implementation using [buildList] var error: Any? = EmptyValue - val results = buildList { - list.forEach { - fold({ - block(it) - }, { newError -> - error = semigroup.emptyCombine(error, newError) - }, { - add(it) - }) - } - } - return when (val e = EmptyValue.unbox(error)) { - null -> results - else -> raise(e) + val results = ArrayList(list.collectionSizeOrDefault(10)) + for (item in list) { + fold, B, Unit>( + { transform(RaiseAccumulate(this), item) }, + { errors -> error = combine(error, errors.reduce(combine), combine) }, + { results.add(it) } + ) } + return if (error === EmptyValue) results else raise(unbox(error)) } /** - * Accumulate the errors obtained by executing the [block] - * over every element of [list]. + * Accumulate the errors obtained by executing the [block] over every element of [list]. */ @RaiseDSL public inline fun Raise>.mapOrAccumulate( list: Iterable, - @BuilderInference crossinline block: Raise.(A) -> B -): List = - mapOrAccumulate(Semigroup.nonEmptyList(), list) { elt -> mapErrorNel { block(elt) } } + @BuilderInference block: RaiseAccumulate.(A) -> B +): List { + val error = mutableListOf() + val results = ArrayList(list.collectionSizeOrDefault(10)) + for (item in list) { + fold, B, Unit>( + { block(RaiseAccumulate(this), item) }, + { errors -> error.addAll(errors) }, + { results.add(it) } + ) + } + return error.toNonEmptyListOrNull()?.let { raise(it) } ?: results +} + /** * Receiver type belonging to [mapOrAccumulate]. @@ -583,24 +452,25 @@ public inline fun Raise>.mapOrAccumulate( */ public open class RaiseAccumulate( public val raise: Raise> -): Raise { +) : Raise { @RaiseDSL public override fun raise(r: Error): Nothing = raise.raise(nonEmptyListOf(r)) @RaiseDSL - public fun EitherNel.bindNel(): A = when(this) { + public fun EitherNel.bindNel(): A = when (this) { is Either.Left -> raise.raise(value) is Either.Right -> value } @RaiseDSL - public fun ValidatedNel.bindNel(): A = when(this) { + public fun ValidatedNel.bindNel(): A = when (this) { is Validated.Invalid -> raise.raise(value) is Validated.Valid -> value } + @RaiseDSL public inline fun withNel(block: Raise>.() -> A): A = block(raise) } From 5ea2cf42bdde62c25db15ad66d0c8ed2479346a3 Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Mon, 6 Mar 2023 14:15:18 +0100 Subject: [PATCH 14/19] Remove @BuilderInference for blocks with no receiver --- .../arrow/core/raise/RaiseAccumulate.kt | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) 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 a67937c9fc2..3ad55d69758 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 @@ -28,7 +28,7 @@ public inline fun Raise.zipOrAccumulate( combine: (R, R) -> R, @BuilderInference action1: RaiseAccumulate.() -> A, @BuilderInference action2: RaiseAccumulate.() -> B, - @BuilderInference block: (A, B) -> C + block: (A, B) -> C ): C = zipOrAccumulate( combine, @@ -47,7 +47,7 @@ public inline fun Raise.zipOrAccumulate( @BuilderInference action1: RaiseAccumulate.() -> A, @BuilderInference action2: RaiseAccumulate.() -> B, @BuilderInference action3: RaiseAccumulate.() -> C, - @BuilderInference block: (A, B, C) -> D + block: (A, B, C) -> D ): D = zipOrAccumulate( combine, @@ -68,7 +68,7 @@ public inline fun Raise.zipOrAccumulate( @BuilderInference action2: RaiseAccumulate.() -> B, @BuilderInference action3: RaiseAccumulate.() -> C, @BuilderInference action4: RaiseAccumulate.() -> D, - @BuilderInference block: (A, B, C, D) -> E + block: (A, B, C, D) -> E ): E = zipOrAccumulate( combine, @@ -91,7 +91,7 @@ public inline fun Raise.zipOrAccumulate( @BuilderInference action3: RaiseAccumulate.() -> C, @BuilderInference action4: RaiseAccumulate.() -> D, @BuilderInference action5: RaiseAccumulate.() -> E, - @BuilderInference block: (A, B, C, D, E) -> F + block: (A, B, C, D, E) -> F ): F = zipOrAccumulate( combine, @@ -116,7 +116,7 @@ public inline fun Raise.zipOrAccumulate( @BuilderInference action4: RaiseAccumulate.() -> D, @BuilderInference action5: RaiseAccumulate.() -> E, @BuilderInference action6: RaiseAccumulate.() -> F, - @BuilderInference block: (A, B, C, D, E, F) -> G + block: (A, B, C, D, E, F) -> G ): G = zipOrAccumulate( combine, @@ -143,7 +143,7 @@ public inline fun Raise.zipOrAccumulate( @BuilderInference action5: RaiseAccumulate.() -> E, @BuilderInference action6: RaiseAccumulate.() -> F, @BuilderInference action7: RaiseAccumulate.() -> G, - @BuilderInference block: (A, B, C, D, E, F, G) -> H + block: (A, B, C, D, E, F, G) -> H ): H = zipOrAccumulate( combine, @@ -172,7 +172,7 @@ public inline fun Raise.zipOrAccumulate( @BuilderInference action6: RaiseAccumulate.() -> F, @BuilderInference action7: RaiseAccumulate.() -> G, @BuilderInference action8: RaiseAccumulate.() -> H, - @BuilderInference block: (A, B, C, D, E, F, G, H) -> I + block: (A, B, C, D, E, F, G, H) -> I ): I = zipOrAccumulate( combine, @@ -203,7 +203,7 @@ public inline fun Raise.zipOrAccumulate( @BuilderInference action7: RaiseAccumulate.() -> G, @BuilderInference action8: RaiseAccumulate.() -> H, @BuilderInference action9: RaiseAccumulate.() -> I, - @BuilderInference block: (A, B, C, D, E, F, G, H, I) -> J + block: (A, B, C, D, E, F, G, H, I) -> J ): J { var error: Any? = EmptyValue val a = recover({ action1(RaiseAccumulate(this)) }) { error = combine(error, it.reduce(combine), combine); EmptyValue } @@ -243,7 +243,7 @@ public inline fun Raise>.zipOrAccumulate( @BuilderInference action1: RaiseAccumulate.() -> A, @BuilderInference action2: RaiseAccumulate.() -> B, @BuilderInference action3: RaiseAccumulate.() -> C, - @BuilderInference block: (A, B, C) -> D + block: (A, B, C) -> D ): D = zipOrAccumulate( action1, @@ -262,7 +262,7 @@ public inline fun Raise>.zipOrAccumulate( @BuilderInference action2: RaiseAccumulate.() -> B, @BuilderInference action3: RaiseAccumulate.() -> C, @BuilderInference action4: RaiseAccumulate.() -> D, - @BuilderInference block: (A, B, C, D) -> E + block: (A, B, C, D) -> E ): E = zipOrAccumulate( action1, @@ -283,7 +283,7 @@ public inline fun Raise>.zipOrAccumulate( @BuilderInference action3: RaiseAccumulate.() -> C, @BuilderInference action4: RaiseAccumulate.() -> D, @BuilderInference action5: RaiseAccumulate.() -> E, - @BuilderInference block: (A, B, C, D, E) -> F + block: (A, B, C, D, E) -> F ): F = zipOrAccumulate( action1, @@ -306,7 +306,7 @@ public inline fun Raise>.zipOrAccumulat @BuilderInference action4: RaiseAccumulate.() -> D, @BuilderInference action5: RaiseAccumulate.() -> E, @BuilderInference action6: RaiseAccumulate.() -> F, - @BuilderInference block: (A, B, C, D, E, F) -> G + block: (A, B, C, D, E, F) -> G ): G = zipOrAccumulate( action1, @@ -331,7 +331,7 @@ public inline fun Raise>.zipOrAccumu @BuilderInference action5: RaiseAccumulate.() -> E, @BuilderInference action6: RaiseAccumulate.() -> F, @BuilderInference action7: RaiseAccumulate.() -> G, - @BuilderInference block: (A, B, C, D, E, F, G) -> H + block: (A, B, C, D, E, F, G) -> H ): H = zipOrAccumulate( action1, @@ -358,7 +358,7 @@ public inline fun Raise>.zipOrAcc @BuilderInference action6: RaiseAccumulate.() -> F, @BuilderInference action7: RaiseAccumulate.() -> G, @BuilderInference action8: RaiseAccumulate.() -> H, - @BuilderInference block: (A, B, C, D, E, F, G, H) -> I + block: (A, B, C, D, E, F, G, H) -> I ): I = zipOrAccumulate( action1, @@ -387,7 +387,7 @@ public inline fun Raise>.zipOr @BuilderInference action7: RaiseAccumulate.() -> G, @BuilderInference action8: RaiseAccumulate.() -> H, @BuilderInference action9: RaiseAccumulate.() -> I, - @BuilderInference block: (A, B, C, D, E, F, G, H, I) -> J + block: (A, B, C, D, E, F, G, H, I) -> J ): J { val error: MutableList = mutableListOf() val a = recover({ action1(RaiseAccumulate(this)) }) { error.addAll(it); EmptyValue } @@ -425,18 +425,18 @@ public inline fun Raise.mapOrAccumulate( } /** - * Accumulate the errors obtained by executing the [block] over every element of [list]. + * Accumulate the errors obtained by executing the [transform] over every element of [list]. */ @RaiseDSL public inline fun Raise>.mapOrAccumulate( list: Iterable, - @BuilderInference block: RaiseAccumulate.(A) -> B + @BuilderInference transform: RaiseAccumulate.(A) -> B ): List { val error = mutableListOf() val results = ArrayList(list.collectionSizeOrDefault(10)) for (item in list) { fold, B, Unit>( - { block(RaiseAccumulate(this), item) }, + { transform(RaiseAccumulate(this), item) }, { errors -> error.addAll(errors) }, { results.add(it) } ) From 4104ae43b37938cbc7b4a4b05d62780f5341f55d Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Fri, 3 Mar 2023 15:35:52 +0100 Subject: [PATCH 15/19] Update RaiseAccumulate --- .../src/commonMain/kotlin/arrow/core/raise/RaiseAccumulate.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 3ad55d69758..1d5649128e0 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 @@ -1,14 +1,15 @@ @file:OptIn(ExperimentalTypeInference::class) @file:JvmMultifileClass @file:JvmName("RaiseKt") + package arrow.core.raise import arrow.core.mapOrAccumulate import arrow.core.Either import arrow.core.EitherNel import arrow.core.EmptyValue -import arrow.core.EmptyValue.combine import arrow.core.EmptyValue.unbox +import arrow.core.EmptyValue.combine import arrow.core.NonEmptyList import arrow.core.Validated import arrow.core.collectionSizeOrDefault From 007a1a0b3f1e12bfac9d52da31776eb184f59e97 Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Fri, 3 Mar 2023 15:58:54 +0100 Subject: [PATCH 16/19] Update Either, and re-use code from Raise DSL --- arrow-libs/core/arrow-core/api/arrow-core.api | 43 +- .../commonMain/kotlin/arrow/core/Either.kt | 641 +++++------------- .../kotlin/arrow/core/EitherTest.kt | 72 +- 3 files changed, 233 insertions(+), 523 deletions(-) diff --git a/arrow-libs/core/arrow-core/api/arrow-core.api b/arrow-libs/core/arrow-core/api/arrow-core.api index 838a55a597a..eccd19495fc 100644 --- a/arrow-libs/core/arrow-core/api/arrow-core.api +++ b/arrow-libs/core/arrow-core/api/arrow-core.api @@ -214,33 +214,22 @@ public final class arrow/core/Either$Companion { public final fun tryCatch (Lkotlin/jvm/functions/Function0;)Larrow/core/Either; public final fun tryCatch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;)Larrow/core/Either; public final fun tryCatchAndFlatten (Lkotlin/jvm/functions/Function0;)Larrow/core/Either; - public final fun zipOrAccumulate (Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function10;)Larrow/core/Either; - public final fun zipOrAccumulate (Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function9;)Larrow/core/Either; - public final fun zipOrAccumulate (Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function8;)Larrow/core/Either; - public final fun zipOrAccumulate (Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function7;)Larrow/core/Either; - public final fun zipOrAccumulate (Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function6;)Larrow/core/Either; - public final fun zipOrAccumulate (Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function5;)Larrow/core/Either; - public final fun zipOrAccumulate (Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function4;)Larrow/core/Either; - public final fun zipOrAccumulate (Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function3;)Larrow/core/Either; - public final fun zipOrAccumulate (Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function2;)Larrow/core/Either; - public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function2;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function10;)Larrow/core/Either; - public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function2;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function9;)Larrow/core/Either; - public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function2;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function8;)Larrow/core/Either; - public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function2;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function7;)Larrow/core/Either; - public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function2;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function6;)Larrow/core/Either; - public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function2;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function5;)Larrow/core/Either; - public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function2;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function4;)Larrow/core/Either; - public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function2;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function3;)Larrow/core/Either; - public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function2;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function2;)Larrow/core/Either; - public final fun zipOrAccumulateNonEmptyList (Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function10;)Larrow/core/Either; - public final fun zipOrAccumulateNonEmptyList (Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function9;)Larrow/core/Either; - public final fun zipOrAccumulateNonEmptyList (Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function8;)Larrow/core/Either; - public final fun zipOrAccumulateNonEmptyList (Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function7;)Larrow/core/Either; - public final fun zipOrAccumulateNonEmptyList (Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function6;)Larrow/core/Either; - public final fun zipOrAccumulateNonEmptyList (Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function5;)Larrow/core/Either; - public final fun zipOrAccumulateNonEmptyList (Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function4;)Larrow/core/Either; - public final fun zipOrAccumulateNonEmptyList (Larrow/core/Either;Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function3;)Larrow/core/Either; - public final fun zipOrAccumulateNonEmptyList (Larrow/core/Either;Larrow/core/Either;Lkotlin/jvm/functions/Function2;)Larrow/core/Either; + public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function9;)Larrow/core/Either; + public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function8;)Larrow/core/Either; + public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function7;)Larrow/core/Either; + public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function6;)Larrow/core/Either; + public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;)Larrow/core/Either; + public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function4;)Larrow/core/Either; + public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;)Larrow/core/Either; + public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Larrow/core/Either; + public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function9;)Larrow/core/Either; + public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function8;)Larrow/core/Either; + public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function7;)Larrow/core/Either; + public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function6;)Larrow/core/Either; + public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function5;)Larrow/core/Either; + public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function4;)Larrow/core/Either; + public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;)Larrow/core/Either; + public final fun zipOrAccumulate (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Larrow/core/Either; } public final class arrow/core/Either$Left : arrow/core/Either { diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Either.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Either.kt index 198cfef509f..1a8bf673e4c 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Either.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Either.kt @@ -1,18 +1,17 @@ -@file:OptIn(ExperimentalContracts::class) +@file:OptIn(ExperimentalContracts::class, ExperimentalTypeInference::class) package arrow.core import arrow.core.Either.Companion.resolve import arrow.core.Either.Left import arrow.core.Either.Right -import arrow.core.Either.Right.Companion.unit -import arrow.core.computations.ResultEffect.bind -import arrow.core.continuations.Eager import arrow.core.continuations.EagerEffect import arrow.core.continuations.Effect -import arrow.core.continuations.Token import arrow.core.raise.Raise +import arrow.core.raise.RaiseAccumulate +import arrow.core.raise.catch import arrow.core.raise.either +import arrow.core.raise.zipOrAccumulate import arrow.typeclasses.Monoid import arrow.typeclasses.Semigroup import kotlin.contracts.ExperimentalContracts @@ -1378,14 +1377,8 @@ public sealed class Either { @JvmStatic @JvmName("tryCatch") - public inline fun catch(f: () -> R): Either { - contract { callsInPlace(f, InvocationKind.EXACTLY_ONCE) } - return try { - f().right() - } catch (t: Throwable) { - t.nonFatalOrThrow().left() - } - } + public inline fun catch(f: () -> R): Either = + catch({ f().right() }) { e -> e.left() } @Deprecated( RedundantAPI + "Compose catch with flatten instead", @@ -1481,474 +1474,196 @@ public sealed class Either { public fun lift(fa: (A) -> C, fb: (B) -> D): (Either) -> Either = { it.bimap(fa, fb) } - - public inline fun zipOrAccumulate( - combine: (E, E) -> E, - a: Either, - b: Either, - transform: (A, B) -> Z, - ): Either { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(combine, a, b, unit, unit, unit, unit, unit, unit, unit, unit) { aa, bb, _, _, _, _, _, _, _, _ -> - transform(aa, bb) - } - } - - public inline fun zipOrAccumulate( - combine: (E, E) -> E, - a: Either, - b: Either, - c: Either, - transform: (A, B, C) -> Z, - ): Either { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(combine, a, b, c, unit, unit, unit, unit, unit, unit, unit) { aa, bb, cc, _, _, _, _, _, _, _ -> - transform(aa, bb, cc) - } - } - - public inline fun zipOrAccumulate( - combine: (E, E) -> E, - a: Either, - b: Either, - c: Either, - d: Either, - transform: (A, B, C, D) -> Z, - ): Either { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(combine, a, b, c, d, unit, unit, unit, unit, unit, unit) { aa, bb, cc, dd, _, _, _, _, _, _ -> - transform(aa, bb, cc, dd) - } - } - - public inline fun zipOrAccumulate( - combine: (E, E) -> E, - a: Either, - b: Either, - c: Either, - d: Either, - e: Either, - transform: (A, B, C, D, EE) -> Z, - ): Either { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(combine, a, b, c, d, e, unit, unit, unit, unit, unit) { aa, bb, cc, dd, ee, _, _, _, _, _ -> - transform(aa, bb, cc, dd, ee) - } - } - - public inline fun zipOrAccumulate( - combine: (E, E) -> E, - a: Either, - b: Either, - c: Either, - d: Either, - e: Either, - f: Either, - transform: (A, B, C, D, EE, FF) -> Z, - ): Either { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(combine, a, b, c, d, e, f, unit, unit, unit, unit) { aa, bb, cc, dd, ee, ff, _, _, _, _ -> - transform(aa, bb, cc, dd, ee, ff) - } - } - - public inline fun zipOrAccumulate( - combine: (E, E) -> E, - a: Either, - b: Either, - c: Either, - d: Either, - e: Either, - f: Either, - g: Either, - transform: (A, B, C, D, EE, F, G) -> Z, - ): Either { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(combine, a, b, c, d, e, f, g, unit, unit, unit) { aa, bb, cc, dd, ee, ff, gg, _, _, _ -> - transform(aa, bb, cc, dd, ee, ff, gg) - } - } - - public inline fun zipOrAccumulate( - combine: (E, E) -> E, - a: Either, - b: Either, - c: Either, - d: Either, - e: Either, - f: Either, - g: Either, - h: Either, - transform: (A, B, C, D, EE, F, G, H) -> Z, - ): Either { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(combine, a, b, c, d, e, f, g, h, unit, unit) { aa, bb, cc, dd, ee, ff, gg, hh, _, _ -> - transform(aa, bb, cc, dd, ee, ff, gg, hh) - } - } - - public inline fun zipOrAccumulate( - combine: (E, E) -> E, - a: Either, - b: Either, - c: Either, - d: Either, - e: Either, - f: Either, - g: Either, - h: Either, - i: Either, - transform: (A, B, C, D, EE, F, G, H, I) -> Z, - ): Either { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(combine, a, b, c, d, e, f, g, h, i, unit) { aa, bb, cc, dd, ee, ff, gg, hh, ii, _ -> - transform(aa, bb, cc, dd, ee, ff, gg, hh, ii) - } - } - - @Suppress("DuplicatedCode") - public inline fun zipOrAccumulate( - combine: (E, E) -> E, - a: Either, - b: Either, - c: Either, - d: Either, - e: Either, - f: Either, - g: Either, - h: Either, - i: Either, - j: Either, - transform: (A, B, C, D, EE, F, G, H, I, J) -> Z, - ): Either { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return if (a is Right && b is Right && c is Right && d is Right && e is Right && f is Right && g is Right && h is Right && i is Right && j is Right) { - Right(transform(a.value, b.value, c.value, d.value, e.value, f.value, g.value, h.value, i.value, j.value)) - } else { - var accumulatedError: Any? = EmptyValue - accumulatedError = if (a is Left) a.value else accumulatedError - accumulatedError = if (b is Left) EmptyValue.combine(accumulatedError, b.value, combine) else accumulatedError - accumulatedError = if (c is Left) EmptyValue.combine(accumulatedError, c.value, combine) else accumulatedError - accumulatedError = if (d is Left) EmptyValue.combine(accumulatedError, d.value, combine) else accumulatedError - accumulatedError = if (e is Left) EmptyValue.combine(accumulatedError, e.value, combine) else accumulatedError - accumulatedError = if (f is Left) EmptyValue.combine(accumulatedError, f.value, combine) else accumulatedError - accumulatedError = if (g is Left) EmptyValue.combine(accumulatedError, g.value, combine) else accumulatedError - accumulatedError = if (h is Left) EmptyValue.combine(accumulatedError, h.value, combine) else accumulatedError - accumulatedError = if (i is Left) EmptyValue.combine(accumulatedError, i.value, combine) else accumulatedError - accumulatedError = if (j is Left) EmptyValue.combine(accumulatedError, j.value, combine) else accumulatedError - - @Suppress("UNCHECKED_CAST") - (Left(accumulatedError as E)) - } - } - - public inline fun zipOrAccumulate( - a: Either, - b: Either, - transform: (A, B) -> Z, - ): Either, Z> { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(a, b, unit, unit, unit, unit, unit, unit, unit, unit) { aa, bb, _, _, _, _, _, _, _, _ -> - transform(aa, bb) - } - } - - public inline fun zipOrAccumulate( - a: Either, - b: Either, - c: Either, - transform: (A, B, C) -> Z, - ): Either, Z> { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(a, b, c, unit, unit, unit, unit, unit, unit, unit) { aa, bb, cc, _, _, _, _, _, _, _ -> - transform(aa, bb, cc) - } + public inline fun zipOrAccumulate( + combine: (R, R) -> R, + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + block: (A, B) -> C + ): Either = either { + zipOrAccumulate(combine, action1, action2, block) } - public inline fun zipOrAccumulate( - a: Either, - b: Either, - c: Either, - d: Either, - transform: (A, B, C, D) -> Z, - ): Either, Z> { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(a, b, c, d, unit, unit, unit, unit, unit, unit) { aa, bb, cc, dd, _, _, _, _, _, _ -> - transform(aa, bb, cc, dd) - } + public inline fun zipOrAccumulate( + combine: (R, R) -> R, + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + block: (A, B, C) -> D + ): Either = either { + zipOrAccumulate(combine, action1, action2, action3, block) } - public inline fun zipOrAccumulate( - a: Either, - b: Either, - c: Either, - d: Either, - e: Either, - transform: (A, B, C, D, EE) -> Z, - ): Either, Z> { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(a, b, c, d, e, unit, unit, unit, unit, unit) { aa, bb, cc, dd, ee, _, _, _, _, _ -> - transform(aa, bb, cc, dd, ee) - } + public inline fun zipOrAccumulate( + combine: (R, R) -> R, + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + block: (A, B, C, D) -> E + ): Either = either { + zipOrAccumulate(combine, action1, action2, action3, action4, block) } - public inline fun zipOrAccumulate( - a: Either, - b: Either, - c: Either, - d: Either, - e: Either, - f: Either, - transform: (A, B, C, D, EE, FF) -> Z, - ): Either, Z> { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(a, b, c, d, e, f, unit, unit, unit, unit) { aa, bb, cc, dd, ee, ff, _, _, _, _ -> - transform(aa, bb, cc, dd, ee, ff) - } + public inline fun zipOrAccumulate( + combine: (R, R) -> R, + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + block: (A, B, C, D, E) -> F + ): Either = either { + zipOrAccumulate(combine, action1, action2, action3, action4, action5, block) } - public inline fun zipOrAccumulate( - a: Either, - b: Either, - c: Either, - d: Either, - e: Either, - f: Either, - g: Either, - transform: (A, B, C, D, EE, F, G) -> Z, - ): Either, Z> { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(a, b, c, d, e, f, g, unit, unit, unit) { aa, bb, cc, dd, ee, ff, gg, _, _, _ -> - transform(aa, bb, cc, dd, ee, ff, gg) - } - } - - public inline fun zipOrAccumulate( - a: Either, - b: Either, - c: Either, - d: Either, - e: Either, - f: Either, - g: Either, - h: Either, - transform: (A, B, C, D, EE, F, G, H) -> Z, - ): Either, Z> { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(a, b, c, d, e, f, g, h, unit, unit) { aa, bb, cc, dd, ee, ff, gg, hh, _, _ -> - transform(aa, bb, cc, dd, ee, ff, gg, hh) - } + public inline fun zipOrAccumulate( + combine: (R, R) -> R, + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + @BuilderInference action6: RaiseAccumulate.() -> F, + block: (A, B, C, D, E, F) -> G + ): Either = either { + zipOrAccumulate(combine, action1, action2, action3, action4, action5, action6, block) } - public inline fun zipOrAccumulate( - a: Either, - b: Either, - c: Either, - d: Either, - e: Either, - f: Either, - g: Either, - h: Either, - i: Either, - transform: (A, B, C, D, EE, F, G, H, I) -> Z, - ): Either, Z> { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(a, b, c, d, e, f, g, h, i, unit) { aa, bb, cc, dd, ee, ff, gg, hh, ii, _ -> - transform(aa, bb, cc, dd, ee, ff, gg, hh, ii) - } + public inline fun zipOrAccumulate( + combine: (R, R) -> R, + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + @BuilderInference action6: RaiseAccumulate.() -> F, + @BuilderInference action7: RaiseAccumulate.() -> G, + block: (A, B, C, D, E, F, G) -> H + ): Either = either { + zipOrAccumulate(combine, action1, action2, action3, action4, action5, action6, action7, block) } - @Suppress("DuplicatedCode") - public inline fun zipOrAccumulate( - a: Either, - b: Either, - c: Either, - d: Either, - e: Either, - f: Either, - g: Either, - h: Either, - i: Either, - j: Either, - transform: (A, B, C, D, EE, F, G, H, I, J) -> Z, - ): Either, Z> { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return if (a is Right && b is Right && c is Right && d is Right && e is Right && f is Right && g is Right && h is Right && i is Right && j is Right) { - Right(transform(a.value, b.value, c.value, d.value, e.value, f.value, g.value, h.value, i.value, j.value)) - } else { - val list = buildList(9) { - if (a is Left) add(a.value) - if (b is Left) add(b.value) - if (c is Left) add(c.value) - if (d is Left) add(d.value) - if (e is Left) add(e.value) - if (f is Left) add(f.value) - if (g is Left) add(g.value) - if (h is Left) add(h.value) - if (i is Left) add(i.value) - if (j is Left) add(j.value) - } - Left(NonEmptyList(list[0], list.drop(1))) - } + public inline fun zipOrAccumulate( + combine: (R, R) -> R, + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + @BuilderInference action6: RaiseAccumulate.() -> F, + @BuilderInference action7: RaiseAccumulate.() -> G, + @BuilderInference action8: RaiseAccumulate.() -> H, + block: (A, B, C, D, E, F, G, H) -> I + ): Either = either { + zipOrAccumulate(combine, action1, action2, action3, action4, action5, action6, action7, action8, block) } - @JvmName("zipOrAccumulateNonEmptyList") - public inline fun zipOrAccumulate( - a: EitherNel, - b: EitherNel, - transform: (A, B) -> Z, - ): EitherNel { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(a, b, unit, unit, unit, unit, unit, unit, unit, unit) { aa, bb, _, _, _, _, _, _, _, _ -> - transform(aa, bb) - } + public inline fun zipOrAccumulate( + combine: (R, R) -> R, + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + @BuilderInference action6: RaiseAccumulate.() -> F, + @BuilderInference action7: RaiseAccumulate.() -> G, + @BuilderInference action8: RaiseAccumulate.() -> H, + @BuilderInference action9: RaiseAccumulate.() -> I, + block: (A, B, C, D, E, F, G, H, I) -> J + ): Either = either { + zipOrAccumulate(combine, action1, action2, action3, action4, action5, action6, action7, action8, action9, block) } - @JvmName("zipOrAccumulateNonEmptyList") - public inline fun zipOrAccumulate( - a: EitherNel, - b: EitherNel, - c: EitherNel, - transform: (A, B, C) -> Z, - ): EitherNel { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(a, b, c, unit, unit, unit, unit, unit, unit, unit) { aa, bb, cc, _, _, _, _, _, _, _ -> - transform(aa, bb, cc) - } + public inline fun zipOrAccumulate( + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + block: (A, B) -> C, + ): Either, C> = either { + zipOrAccumulate(action1, action2, block) } - @JvmName("zipOrAccumulateNonEmptyList") - public inline fun zipOrAccumulate( - a: EitherNel, - b: EitherNel, - c: EitherNel, - d: EitherNel, - transform: (A, B, C, D) -> Z, - ): EitherNel { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(a, b, c, d, unit, unit, unit, unit, unit, unit) { aa, bb, cc, dd, _, _, _, _, _, _ -> - transform(aa, bb, cc, dd) - } + public inline fun zipOrAccumulate( + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + block: (A, B, C) -> D, + ): Either, D> = either { + zipOrAccumulate(action1, action2, action3, block) } - @JvmName("zipOrAccumulateNonEmptyList") - public inline fun zipOrAccumulate( - a: EitherNel, - b: EitherNel, - c: EitherNel, - d: EitherNel, - e: EitherNel, - transform: (A, B, C, D, EE) -> Z, - ): EitherNel { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(a, b, c, d, e, unit, unit, unit, unit, unit) { aa, bb, cc, dd, ee, _, _, _, _, _ -> - transform(aa, bb, cc, dd, ee) - } + public inline fun zipOrAccumulate( + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + block: (A, B, C, D) -> E, + ): Either, E> = either { + zipOrAccumulate(action1, action2, action3, action4, block) } - @JvmName("zipOrAccumulateNonEmptyList") - public inline fun zipOrAccumulate( - a: EitherNel, - b: EitherNel, - c: EitherNel, - d: EitherNel, - e: EitherNel, - f: EitherNel, - transform: (A, B, C, D, EE, FF) -> Z, - ): EitherNel { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(a, b, c, d, e, f, unit, unit, unit, unit) { aa, bb, cc, dd, ee, ff, _, _, _, _ -> - transform(aa, bb, cc, dd, ee, ff) - } + public inline fun zipOrAccumulate( + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + block: (A, B, C, D, E) -> F, + ): Either, F> = either { + zipOrAccumulate(action1, action2, action3, action4, action5, block) } - @JvmName("zipOrAccumulateNonEmptyList") - public inline fun zipOrAccumulate( - a: EitherNel, - b: EitherNel, - c: EitherNel, - d: EitherNel, - e: EitherNel, - f: EitherNel, - g: EitherNel, - transform: (A, B, C, D, EE, F, G) -> Z, - ): EitherNel { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(a, b, c, d, e, f, g, unit, unit, unit) { aa, bb, cc, dd, ee, ff, gg, _, _, _ -> - transform(aa, bb, cc, dd, ee, ff, gg) - } + public inline fun zipOrAccumulate( + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + @BuilderInference action6: RaiseAccumulate.() -> F, + block: (A, B, C, D, E, F) -> G, + ): Either, G> = either { + zipOrAccumulate(action1, action2, action3, action4, action5, action6, block) } - @JvmName("zipOrAccumulateNonEmptyList") - public inline fun zipOrAccumulate( - a: EitherNel, - b: EitherNel, - c: EitherNel, - d: EitherNel, - e: EitherNel, - f: EitherNel, - g: EitherNel, - h: EitherNel, - transform: (A, B, C, D, EE, F, G, H) -> Z, - ): EitherNel { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(a, b, c, d, e, f, g, h, unit, unit) { aa, bb, cc, dd, ee, ff, gg, hh, _, _ -> - transform(aa, bb, cc, dd, ee, ff, gg, hh) - } + public inline fun zipOrAccumulate( + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + @BuilderInference action6: RaiseAccumulate.() -> F, + @BuilderInference action7: RaiseAccumulate.() -> G, + block: (A, B, C, D, E, F, G) -> H, + ): Either, H> = either { + zipOrAccumulate(action1, action2, action3, action4, action5, action6, action7, block) } - @JvmName("zipOrAccumulateNonEmptyList") - public inline fun zipOrAccumulate( - a: EitherNel, - b: EitherNel, - c: EitherNel, - d: EitherNel, - e: EitherNel, - f: EitherNel, - g: EitherNel, - h: EitherNel, - i: EitherNel, - transform: (A, B, C, D, EE, F, G, H, I) -> Z, - ): EitherNel { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return zipOrAccumulate(a, b, c, d, e, f, g, h, i, unit) { aa, bb, cc, dd, ee, ff, gg, hh, ii, _ -> - transform(aa, bb, cc, dd, ee, ff, gg, hh, ii) - } + public inline fun zipOrAccumulate( + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + @BuilderInference action6: RaiseAccumulate.() -> F, + @BuilderInference action7: RaiseAccumulate.() -> G, + @BuilderInference action8: RaiseAccumulate.() -> H, + block: (A, B, C, D, E, F, G, H) -> I, + ): Either, I> = either { + zipOrAccumulate(action1, action2, action3, action4, action5, action6, action7, action8, block) } - @Suppress("DuplicatedCode") - @JvmName("zipOrAccumulateNonEmptyList") - public inline fun zipOrAccumulate( - a: EitherNel, - b: EitherNel, - c: EitherNel, - d: EitherNel, - e: EitherNel, - f: EitherNel, - g: EitherNel, - h: EitherNel, - i: EitherNel, - j: EitherNel, - transform: (A, B, C, D, EE, F, G, H, I, J) -> Z, - ): EitherNel { - contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } - return if (a is Right && b is Right && c is Right && d is Right && e is Right && f is Right && g is Right && h is Right && i is Right && j is Right) { - Right(transform(a.value, b.value, c.value, d.value, e.value, f.value, g.value, h.value, i.value, j.value)) - } else { - val list = buildList { - if (a is Left) addAll(a.value) - if (b is Left) addAll(b.value) - if (c is Left) addAll(c.value) - if (d is Left) addAll(d.value) - if (e is Left) addAll(e.value) - if (f is Left) addAll(f.value) - if (g is Left) addAll(g.value) - if (h is Left) addAll(h.value) - if (i is Left) addAll(i.value) - if (j is Left) addAll(j.value) - } - Left(NonEmptyList(list[0], list.drop(1))) - } + public inline fun zipOrAccumulate( + @BuilderInference action1: RaiseAccumulate.() -> A, + @BuilderInference action2: RaiseAccumulate.() -> B, + @BuilderInference action3: RaiseAccumulate.() -> C, + @BuilderInference action4: RaiseAccumulate.() -> D, + @BuilderInference action5: RaiseAccumulate.() -> E, + @BuilderInference action6: RaiseAccumulate.() -> F, + @BuilderInference action7: RaiseAccumulate.() -> G, + @BuilderInference action8: RaiseAccumulate.() -> H, + @BuilderInference action9: RaiseAccumulate.() -> I, + block: (A, B, C, D, E, F, G, H, I) -> J + ): Either, J> = either { + zipOrAccumulate(action1, action2, action3, action4, action5, action6, action7, action8, action9, block) } } @@ -2294,10 +2009,13 @@ public operator fun , B : Comparable> Either.compareT @Deprecated( RedundantAPI + "Prefer zipOrAccumulate", - ReplaceWith("Either.zipOrAccumulate({ a, bb -> SGA.run { a.combine(bb) } }, this, b) { a, bb -> SGB.run { a.combine(bb) } }") + ReplaceWith("Either.zipOrAccumulate({ a, bb -> SGA.run { a.combine(bb) } }, { this@combine.bind() }, { b.bind() }) { a, bb -> SGB.run { a.combine(bb) } }") ) public fun Either.combine(SGA: Semigroup, SGB: Semigroup, b: Either): Either = - Either.zipOrAccumulate({ a, bb -> SGA.run { a.combine(bb) } }, this, b) { a, bb -> SGB.run { a.combine(bb) } } + Either.zipOrAccumulate( + { a, bb -> SGA.run { a.combine(bb) } }, + { this@combine.bind() }, + { b.bind() }) { a, bb -> SGB.run { a.combine(bb) } } @Deprecated( RedundantAPI + "Prefer explicit fold instead", @@ -2505,7 +2223,20 @@ public inline fun Either.zip( map: (B, C, D, E, F, G, H, I, J, K) -> L, ): Either { contract { callsInPlace(map, InvocationKind.AT_MOST_ONCE) } - return either { map(bind(), c.bind(), d.bind(), e.bind(), f.bind(), g.bind(), h.bind(), i.bind(), j.bind(), k.bind()) } + return either { + map( + bind(), + c.bind(), + d.bind(), + e.bind(), + f.bind(), + g.bind(), + h.bind(), + i.bind(), + j.bind(), + k.bind() + ) + } } @Deprecated( @@ -2664,7 +2395,7 @@ public fun E.leftNel(): EitherNel = @OptIn(ExperimentalTypeInference::class) public inline fun Either.recover(@BuilderInference recover: Raise.(E) -> A): Either { contract { callsInPlace(recover, InvocationKind.AT_MOST_ONCE) } - return when(this) { + return when (this) { is Left -> either { recover(this, value) } is Right -> this@recover } diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt index 7accba25689..bb8a6e7851c 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt @@ -6,6 +6,7 @@ import arrow.core.test.any import arrow.core.test.either import arrow.core.test.intSmall import arrow.core.test.laws.MonoidLaws +import arrow.core.test.nonEmptyList import arrow.core.test.suspendFunThatReturnsAnyLeft import arrow.core.test.suspendFunThatReturnsAnyRight import arrow.core.test.suspendFunThatReturnsEitherAnyOrAnyOrThrows @@ -32,7 +33,7 @@ import io.kotest.property.arbitrary.short import io.kotest.property.checkAll class EitherTest : StringSpec({ - + val ARB = Arb.either(Arb.string(), Arb.int()) testLaws( @@ -602,7 +603,18 @@ class EitherTest : StringSpec({ Arb.either(Arb.string(), Arb.string()), Arb.either(Arb.string(), Arb.boolean()) ) { a, b, c, d, e, f, g, h, i -> - val res = Either.zipOrAccumulate({ e1, e2 -> "$e1$e2" }, a, b, c, d, e, f, g, h, i, ::Tuple9) + val res = Either.zipOrAccumulate( + { e1, e2 -> "$e1$e2" }, + { a.bind() }, + { b.bind() }, + { c.bind() }, + { d.bind() }, + { e.bind() }, + { f.bind() }, + { g.bind() }, + { h.bind() }, + { i.bind() } + ) { aa, bb, cc, dd, ee, ff, gg, hh, ii -> Tuple9(aa, bb, cc, dd, ee, ff, gg, hh, ii) } val all = listOf(a, b, c, d, e, f, g, h, i) val expected = if (all.any { it.isLeft() }) { @@ -620,52 +632,30 @@ class EitherTest : StringSpec({ "zipOrAccumulate without Semigroup results in all Right transformed, or all Left in a NonEmptyList" { checkAll( Arb.either(Arb.string(), Arb.short()), - Arb.either(Arb.string(), Arb.byte()), - Arb.either(Arb.string(), Arb.int()), - Arb.either(Arb.string(), Arb.long()), - Arb.either(Arb.string(), Arb.float()), - Arb.either(Arb.string(), Arb.double()), - Arb.either(Arb.string(), Arb.char()), - Arb.either(Arb.string(), Arb.string()), - Arb.either(Arb.string(), Arb.boolean()) - ) { a, b, c, d, e, f, g, h, i -> - val res = Either.zipOrAccumulate(a, b, c, d, e, f, g, h, i, ::Tuple9) - val all = listOf(a, b, c, d, e, f, g, h, i) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>().map { it.value }.toNonEmptyListOrNull()!!.left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple9(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8]).right() - } - } - - res shouldBe expected - } - } - - "zipOrAccumulate EitherNel results in all Right transformed, or all Left in a NonEmptyList" { - fun Arb.Companion.nonEmptyList(arb: Arb): Arb> = - Arb.list(arb, 1..100).map { it.toNonEmptyListOrNull()!! } - - checkAll( - Arb.either(Arb.nonEmptyList(Arb.string()), Arb.short()), Arb.either(Arb.nonEmptyList(Arb.string()), Arb.byte()), - Arb.either(Arb.nonEmptyList(Arb.string()), Arb.int()), + Arb.either(Arb.string(), Arb.int()), Arb.either(Arb.nonEmptyList(Arb.string()), Arb.long()), - Arb.either(Arb.nonEmptyList(Arb.string()), Arb.float()), + Arb.either(Arb.string(), Arb.float()), Arb.either(Arb.nonEmptyList(Arb.string()), Arb.double()), - Arb.either(Arb.nonEmptyList(Arb.string()), Arb.char()), + Arb.either(Arb.string(), Arb.char()), Arb.either(Arb.nonEmptyList(Arb.string()), Arb.string()), - Arb.either(Arb.nonEmptyList(Arb.string()), Arb.boolean()) + Arb.either(Arb.string(), Arb.boolean()) ) { a, b, c, d, e, f, g, h, i -> - val res = Either.zipOrAccumulate(a, b, c, d, e, f, g, h, i, ::Tuple9) - val all = listOf(a, b, c, d, e, f, g, h, i) + val res = Either.zipOrAccumulate( + { a.bind() }, + { b.bindNel() }, + { c.bind() }, + { d.bindNel() }, + { e.bind() }, + { f.bindNel() }, + { g.bind() }, + { h.bindNel() }, + { i.bind() } + ) { aa, bb, cc, dd, ee, ff, gg, hh, ii -> Tuple9(aa, bb, cc, dd, ee, ff, gg, hh, ii) } + val all = listOf(a.toEitherNel(), b, c.toEitherNel(), d, e.toEitherNel(), f, g.toEitherNel(), h, i.toEitherNel()) val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>>() - .flatMap { it.value } - .toNonEmptyListOrNull()!!.left() + all.filterIsInstance>>().flatMap { it.value }.toNonEmptyListOrNull()!!.left() } else { all.filterIsInstance>().map { it.value }.let { Tuple9(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8]).right() From 9994be8b91827b342af2b6a488aee5d4d29ab717 Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Mon, 6 Mar 2023 16:04:32 +0100 Subject: [PATCH 17/19] Remove raise for Nel --- .../src/commonTest/kotlin/arrow/core/EitherTest.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt index bb8a6e7851c..bf646454c12 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt @@ -650,8 +650,9 @@ class EitherTest : StringSpec({ { f.bindNel() }, { g.bind() }, { h.bindNel() }, - { i.bind() } - ) { aa, bb, cc, dd, ee, ff, gg, hh, ii -> Tuple9(aa, bb, cc, dd, ee, ff, gg, hh, ii) } + { i.bind() }, + ::Tuple9 + ) val all = listOf(a.toEitherNel(), b, c.toEitherNel(), d, e.toEitherNel(), f, g.toEitherNel(), h, i.toEitherNel()) val expected = if (all.any { it.isLeft() }) { From df86bf94ada377d578b43ae187311e13b85e5d8c Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Wed, 8 Mar 2023 09:03:54 +0100 Subject: [PATCH 18/19] Revert 2 unrelated changes in EitherTest --- .../src/commonTest/kotlin/arrow/core/EitherTest.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt index bf646454c12..35883a1181b 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt @@ -604,7 +604,7 @@ class EitherTest : StringSpec({ Arb.either(Arb.string(), Arb.boolean()) ) { a, b, c, d, e, f, g, h, i -> val res = Either.zipOrAccumulate( - { e1, e2 -> "$e1$e2" }, + String::plus, { a.bind() }, { b.bind() }, { c.bind() }, @@ -613,8 +613,9 @@ class EitherTest : StringSpec({ { f.bind() }, { g.bind() }, { h.bind() }, - { i.bind() } - ) { aa, bb, cc, dd, ee, ff, gg, hh, ii -> Tuple9(aa, bb, cc, dd, ee, ff, gg, hh, ii) } + { i.bind() }, + ::Tuple9 + ) val all = listOf(a, b, c, d, e, f, g, h, i) val expected = if (all.any { it.isLeft() }) { @@ -656,7 +657,9 @@ class EitherTest : StringSpec({ val all = listOf(a.toEitherNel(), b, c.toEitherNel(), d, e.toEitherNel(), f, g.toEitherNel(), h, i.toEitherNel()) val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>>().flatMap { it.value }.toNonEmptyListOrNull()!!.left() + all.filterIsInstance>>() + .flatMap { it.value } + .toNonEmptyListOrNull()!!.left() } else { all.filterIsInstance>().map { it.value }.let { Tuple9(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8]).right() From e222b32a1a9c21a6d44b8727086d2c54a4c4571b Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Wed, 8 Mar 2023 09:04:33 +0100 Subject: [PATCH 19/19] Revert one more unrelated change --- .../arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt index 35883a1181b..4b137d53412 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt @@ -604,7 +604,7 @@ class EitherTest : StringSpec({ Arb.either(Arb.string(), Arb.boolean()) ) { a, b, c, d, e, f, g, h, i -> val res = Either.zipOrAccumulate( - String::plus, + { e1, e2 -> "$e1$e2" }, { a.bind() }, { b.bind() }, { c.bind() },