Skip to content

Commit

Permalink
Add Either#handleFailureWith (#101)
Browse files Browse the repository at this point in the history
  • Loading branch information
cwmyers authored Aug 5, 2024
1 parent a8c6061 commit e5b6bad
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/api/lib.api
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ public final class app/cash/quiver/extensions/EitherKt {
public static final fun flatTap (Larrow/core/Either;Lkotlin/jvm/functions/Function1;)Larrow/core/Either;
public static final fun forEach (Larrow/core/Either;Lkotlin/jvm/functions/Function1;)V
public static final fun getRightUnit ()Larrow/core/Either;
public static final fun handleErrorWith (Larrow/core/Either;Lkotlin/jvm/functions/Function1;)Larrow/core/Either;
public static final fun leftAsOption (Larrow/core/Either;)Larrow/core/Option;
public static final fun leftForEach (Larrow/core/Either;Lkotlin/jvm/functions/Function1;)V
public static final fun mapOption (Larrow/core/Either;Lkotlin/jvm/functions/Function1;)Larrow/core/Either;
Expand Down
13 changes: 13 additions & 0 deletions lib/src/main/kotlin/app/cash/quiver/extensions/Either.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ import arrow.core.Some
import arrow.core.flatMap
import arrow.core.getOrElse
import arrow.core.identity
import arrow.core.recover
import arrow.core.right
import arrow.core.toOption
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.experimental.ExperimentalTypeInference
import app.cash.quiver.extensions.traverse as quiverTraverse

Expand Down Expand Up @@ -287,3 +291,12 @@ inline fun <E, A, B> Either<E, A>.traverseOption(transform: (A) -> Option<B>): O
quiverTraverse(transform)

fun <E, A> Either<E, Option<A>>.sequence(): Option<Either<E, A>> = quiverTraverse(::identity)

/**
* Recovers the left side of an Either with the supplied function
*/
@OptIn(ExperimentalContracts::class)
inline fun <A, B, C> Either<A, B>.handleErrorWith(f: (A) -> Either<C, B>): Either<C, B> {
contract { callsInPlace(f, InvocationKind.AT_MOST_ONCE) }
return recover { a -> f(a).bind() }
}
15 changes: 15 additions & 0 deletions lib/src/test/kotlin/app/cash/quiver/extensions/EitherTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,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
import io.kotest.property.Arb
import io.kotest.property.arbitrary.int
Expand Down Expand Up @@ -197,6 +199,19 @@ class EitherTest : StringSpec({
)
}
}

"handleFailureWith recovers errors" {
1.right().handleErrorWith { Either.Left(Throwable("never happens")) }.shouldBeRight(1)
Throwable("sad panda").left().handleErrorWith { Throwable("even sadder").left() }
.shouldBeLeft().message shouldBe "even sadder"

Throwable("sad panda").left().handleErrorWith { 1.right() }
.shouldBeRight(1)

Throwable("sad panda").left().handleErrorWith { it.left() }
.shouldBeLeft().message shouldBe "sad panda"
}

})

fun testParse(s: String): ErrorOr<Int> =
Expand Down

0 comments on commit e5b6bad

Please sign in to comment.