Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds traverse for Result on a List #110

Merged
merged 1 commit into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/api/lib.api
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
17 changes: 16 additions & 1 deletion lib/src/main/kotlin/app/cash/quiver/extensions/Iterable.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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

/**
Expand All @@ -16,12 +17,27 @@ import kotlin.experimental.ExperimentalTypeInference
inline fun <E, A, B> Iterable<A>.traverse(f: (A) -> Either<E, B>): Either<E, List<B>> =
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 <A, B> Iterable<A>.traverse(f: (A) -> Result<B>): Result<List<B>> =
let { l -> result { l.map { f(it).bind() } } }

/**
* Synonym for traverse((A)-> Either<E, B>): Either<E, List<B>>
*/
inline fun <E, A, B> Iterable<A>.traverseEither(f: (A) -> Either<E, B>): Either<E, List<B>> =
traverse(f)

/**
* Synonym for traverse((A)-> Result<B>): Result<List<B>>
*/
inline fun <A, B> Iterable<A>.traverseResult(f: (A) -> Result<B>): Result<List<B>> =
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.
Expand All @@ -43,4 +59,3 @@ inline fun <A, B> Iterable<A>.traverse(f: (A) -> Option<B>): Option<List<B>> {
*/
inline fun <A, B> Iterable<A>.traverseOption(f: (A) -> Option<B>): Option<List<B>> =
traverse(f)

19 changes: 19 additions & 0 deletions lib/src/test/kotlin/app/cash/quiver/extensions/TraverseTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -25,6 +27,11 @@ class TraverseTest : StringSpec({
result shouldBeRight emptyList()
}

"traverse an empty list returns a Success of an empty list" {
val result = emptyList<Int>().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"
Expand All @@ -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")
Expand Down
Loading