Skip to content

Commit cf3250d

Browse files
authored
Adds Result tap and flatTap (#90)
1 parent 846cbfb commit cf3250d

File tree

5 files changed

+45
-1
lines changed

5 files changed

+45
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

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

89
## [0.5.5] - 2024-06-20
910

lib/api/lib.api

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
public final class app/cash/quiver/Absent : app/cash/quiver/Outcome {
22
public static final field INSTANCE Lapp/cash/quiver/Absent;
3+
public fun equals (Ljava/lang/Object;)Z
4+
public fun hashCode ()I
5+
public fun toString ()Ljava/lang/String;
36
}
47

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

273276
public final class app/cash/quiver/extensions/ResultKt {
274277
public static final fun failure (Ljava/lang/Throwable;)Ljava/lang/Object;
278+
public static final fun flatTap (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
275279
public static final fun flatten (Ljava/lang/Object;)Ljava/lang/Object;
276280
public static final fun mapFailure (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
277281
public static final fun orThrow (Ljava/lang/Object;)Ljava/lang/Object;
278282
public static final fun success (Ljava/lang/Object;)Ljava/lang/Object;
283+
public static final fun tap (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
279284
public static final fun toEither (Ljava/lang/Object;)Larrow/core/Either;
280285
public static final fun toResult (Ljava/lang/Object;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;
281286
public static final fun tryCatch (Lkotlin/Result$Companion;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object;

lib/src/main/kotlin/app/cash/quiver/Outcome.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ sealed class Outcome<out E, out A> constructor(val inner: Either<E, Option<A>>)
108108
*/
109109
data class Present<A>(val value: A) : Outcome<Nothing, A>(value.some().right())
110110
data class Failure<E>(val error: E) : Outcome<E, Nothing>(error.left())
111-
object Absent : Outcome<Nothing, Nothing>(None.right())
111+
data object Absent : Outcome<Nothing, Nothing>(None.right())
112112

113113
fun <A> A.present(): Outcome<Nothing, A> = Present(this)
114114
fun <E> E.failure(): Outcome<E, Nothing> = Failure(this)

lib/src/main/kotlin/app/cash/quiver/extensions/Result.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,20 @@ fun <T> Result<Result<T>>.flatten(): Result<T> = flatMap(::identity)
6363
* Map success to Unit, included for consistency with Either.
6464
*/
6565
fun <T> Result<T>.unit() = map { }
66+
67+
/**
68+
* Performs an effect over successes but maps the original value back into
69+
* the Result.
70+
*/
71+
inline fun <A, B> Result<A>.tap(f: (A) -> B): Result<A> = this.map { a ->
72+
f(a)
73+
a
74+
}
75+
76+
/**
77+
* Performs an effect over successes but maps the original value back into
78+
* the Result. This is useful for mixing with validation functions.
79+
*/
80+
inline fun <A> Result<A>.flatTap(f: (A) -> Result<Any>): Result<A> = this.flatMap { a ->
81+
f(a).map { a }
82+
}

lib/src/test/kotlin/app/cash/quiver/extensions/ResultTest.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package app.cash.quiver.extensions
22

3+
import arrow.core.raise.result
34
import io.kotest.assertions.arrow.core.shouldBeLeft
45
import io.kotest.assertions.arrow.core.shouldBeRight
56
import io.kotest.assertions.throwables.shouldThrow
@@ -79,4 +80,24 @@ class ResultTest : StringSpec({
7980
e.failure<Int>().unit() shouldBe e.failure()
8081
}
8182

83+
"tap performs computation but keeps original value" {
84+
val right: Result<String> = "hello".success()
85+
var sideEffect: String? = null
86+
87+
right.tap { a -> sideEffect = "$a world" }.shouldBeSuccess("hello")
88+
sideEffect shouldBe "hello world"
89+
}
90+
91+
"flatTap performs computation but keeps original value" {
92+
val e = RuntimeException("banana")
93+
val right: Result<String> = "hello".success()
94+
var sideEffect: String? = null
95+
96+
right.flatTap { a -> result { sideEffect = "$a world" } }.shouldBeSuccess("hello")
97+
sideEffect shouldBe "hello world"
98+
99+
right.flatTap { Result.failure<Int>(e) }.shouldBeFailure(e)
100+
e.failure<Int>().flatTap { Result.failure(Exception("broken")) }.shouldBeFailure(e)
101+
}
102+
82103
})

0 commit comments

Comments
 (0)