Skip to content

Commit

Permalink
Adds Result tap and flatTap (#90)
Browse files Browse the repository at this point in the history
  • Loading branch information
Synesso authored Jun 20, 2024
1 parent 846cbfb commit cf3250d
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Added
* Adds `Result<T>.unit(): Result<Unit>` as alias for `.map { }` (Jem Mawson)
* Adds `Result<T>.tap` and `Result<T>.flatTap` (Jem Mawson)

## [0.5.5] - 2024-06-20

Expand Down
5 changes: 5 additions & 0 deletions lib/api/lib.api
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
public final class app/cash/quiver/Absent : app/cash/quiver/Outcome {
public static final field INSTANCE Lapp/cash/quiver/Absent;
public fun equals (Ljava/lang/Object;)Z
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class app/cash/quiver/Failure : app/cash/quiver/Outcome {
Expand Down Expand Up @@ -272,10 +275,12 @@ public final class app/cash/quiver/extensions/OptionKt {

public final class app/cash/quiver/extensions/ResultKt {
public static final fun failure (Ljava/lang/Throwable;)Ljava/lang/Object;
public static final fun flatTap (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun flatten (Ljava/lang/Object;)Ljava/lang/Object;
public static final fun mapFailure (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun orThrow (Ljava/lang/Object;)Ljava/lang/Object;
public static final fun success (Ljava/lang/Object;)Ljava/lang/Object;
public static final fun tap (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
public static final fun toEither (Ljava/lang/Object;)Larrow/core/Either;
public static final fun toResult (Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;
public static final fun tryCatch (Lkotlin/Result$Companion;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;
Expand Down
2 changes: 1 addition & 1 deletion lib/src/main/kotlin/app/cash/quiver/Outcome.kt
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ sealed class Outcome<out E, out A> constructor(val inner: Either<E, Option<A>>)
*/
data class Present<A>(val value: A) : Outcome<Nothing, A>(value.some().right())
data class Failure<E>(val error: E) : Outcome<E, Nothing>(error.left())
object Absent : Outcome<Nothing, Nothing>(None.right())
data object Absent : Outcome<Nothing, Nothing>(None.right())

fun <A> A.present(): Outcome<Nothing, A> = Present(this)
fun <E> E.failure(): Outcome<E, Nothing> = Failure(this)
Expand Down
17 changes: 17 additions & 0 deletions lib/src/main/kotlin/app/cash/quiver/extensions/Result.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,20 @@ fun <T> Result<Result<T>>.flatten(): Result<T> = flatMap(::identity)
* Map success to Unit, included for consistency with Either.
*/
fun <T> Result<T>.unit() = map { }

/**
* Performs an effect over successes but maps the original value back into
* the Result.
*/
inline fun <A, B> Result<A>.tap(f: (A) -> B): Result<A> = this.map { a ->
f(a)
a
}

/**
* Performs an effect over successes but maps the original value back into
* the Result. This is useful for mixing with validation functions.
*/
inline fun <A> Result<A>.flatTap(f: (A) -> Result<Any>): Result<A> = this.flatMap { a ->
f(a).map { a }
}
21 changes: 21 additions & 0 deletions lib/src/test/kotlin/app/cash/quiver/extensions/ResultTest.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package app.cash.quiver.extensions

import arrow.core.raise.result
import io.kotest.assertions.arrow.core.shouldBeLeft
import io.kotest.assertions.arrow.core.shouldBeRight
import io.kotest.assertions.throwables.shouldThrow
Expand Down Expand Up @@ -79,4 +80,24 @@ class ResultTest : StringSpec({
e.failure<Int>().unit() shouldBe e.failure()
}

"tap performs computation but keeps original value" {
val right: Result<String> = "hello".success()
var sideEffect: String? = null

right.tap { a -> sideEffect = "$a world" }.shouldBeSuccess("hello")
sideEffect shouldBe "hello world"
}

"flatTap performs computation but keeps original value" {
val e = RuntimeException("banana")
val right: Result<String> = "hello".success()
var sideEffect: String? = null

right.flatTap { a -> result { sideEffect = "$a world" } }.shouldBeSuccess("hello")
sideEffect shouldBe "hello world"

right.flatTap { Result.failure<Int>(e) }.shouldBeFailure(e)
e.failure<Int>().flatTap { Result.failure(Exception("broken")) }.shouldBeFailure(e)
}

})

0 comments on commit cf3250d

Please sign in to comment.