diff --git a/lib/api/lib.api b/lib/api/lib.api index 7ef42f6..2c965ca 100644 --- a/lib/api/lib.api +++ b/lib/api/lib.api @@ -223,8 +223,10 @@ public final class app/cash/quiver/extensions/IorKt { public final class app/cash/quiver/extensions/IterableKt { public static final fun traverse (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Larrow/core/Either; public static final fun traverse (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Larrow/core/Option; + public static final fun traverse (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public static final fun traverseEither (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Larrow/core/Either; public static final fun traverseOption (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Larrow/core/Option; + public static final fun traverseResult (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; } public final class app/cash/quiver/extensions/ListKt { diff --git a/lib/src/main/kotlin/app/cash/quiver/extensions/Iterable.kt b/lib/src/main/kotlin/app/cash/quiver/extensions/Iterable.kt index 7283151..8fb7368 100644 --- a/lib/src/main/kotlin/app/cash/quiver/extensions/Iterable.kt +++ b/lib/src/main/kotlin/app/cash/quiver/extensions/Iterable.kt @@ -5,6 +5,7 @@ import arrow.core.None import arrow.core.Option import arrow.core.Some import arrow.core.raise.either +import arrow.core.raise.result import kotlin.experimental.ExperimentalTypeInference /** @@ -16,12 +17,27 @@ import kotlin.experimental.ExperimentalTypeInference inline fun Iterable.traverse(f: (A) -> Either): Either> = let { l -> either { l.map { f(it).bind() } } } +/** + * Returns a Result of a list of B results of applying the given transform function + * to each element(A) in the original collection. + */ +@OptIn(ExperimentalTypeInference::class) +@OverloadResolutionByLambdaReturnType +inline fun Iterable.traverse(f: (A) -> Result): Result> = + let { l -> result { l.map { f(it).bind() } } } + /** * Synonym for traverse((A)-> Either): Either> */ inline fun Iterable.traverseEither(f: (A) -> Either): Either> = traverse(f) +/** + * Synonym for traverse((A)-> Result): Result> + */ +inline fun Iterable.traverseResult(f: (A) -> Result): Result> = + traverse(f) + /** * Returns an Option of a list of B results of applying the given transform function * to each element(A) in the original collection. @@ -43,4 +59,3 @@ inline fun Iterable.traverse(f: (A) -> Option): Option> { */ inline fun Iterable.traverseOption(f: (A) -> Option): Option> = traverse(f) - diff --git a/lib/src/test/kotlin/app/cash/quiver/extensions/TraverseTest.kt b/lib/src/test/kotlin/app/cash/quiver/extensions/TraverseTest.kt index f2742f8..bcf2682 100644 --- a/lib/src/test/kotlin/app/cash/quiver/extensions/TraverseTest.kt +++ b/lib/src/test/kotlin/app/cash/quiver/extensions/TraverseTest.kt @@ -7,6 +7,8 @@ import io.kotest.assertions.arrow.core.shouldBeLeft import io.kotest.assertions.arrow.core.shouldBeRight import io.kotest.assertions.arrow.core.shouldBeSome import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.result.shouldBeFailure +import io.kotest.matchers.result.shouldBeSuccess import io.kotest.matchers.shouldBe class TraverseTest : StringSpec({ @@ -25,6 +27,11 @@ class TraverseTest : StringSpec({ result shouldBeRight emptyList() } + "traverse an empty list returns a Success of an empty list" { + val result = emptyList().traverse { Result.success(it) } + result shouldBeSuccess emptyList() + } + "traverse a list of integers returns a left of an error" { val result = listOf(1, 2, 3).traverse { Either.Left("error") } result shouldBeLeft "error" @@ -37,11 +44,23 @@ class TraverseTest : StringSpec({ result shouldBeLeft 4 } + "the failure returned is the first failure returned by the function as it maps over the iterable" { + val result = listOf(1, 2, 4, 6, 7).traverse { + if (it < 3) Result.success(it) else Result.failure(Throwable(message = it.toString())) + } + result.shouldBeFailure().message shouldBe "4" + } + "traverseEither a list of integers returns a Right of the list of mapped strings" { val result = listOf(1, 2, 3).traverseEither { Either.Right(it.toString()) } result shouldBeRight listOf("1", "2", "3") } + "traverseResult a list of integers returns a Success of the list of mapped strings" { + val result = listOf(1, 2, 3).traverseResult { Result.success(it.toString()) } + result shouldBeSuccess listOf("1", "2", "3") + } + "traverse a list of integers returns a Some of the list of mapped strings" { val result = listOf(1, 2, 3).traverse { Some(it.toString()) } result shouldBeSome listOf("1", "2", "3")