Skip to content

Commit 0138629

Browse files
committed
Convert Result to an inline value class
1 parent 0605c1a commit 0138629

File tree

13 files changed

+541
-476
lines changed

13 files changed

+541
-476
lines changed

kotlin-result-coroutines/src/commonMain/kotlin/com/github/michaelbull/result/coroutines/CoroutineBinding.kt

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package com.github.michaelbull.result.coroutines
22

3-
import com.github.michaelbull.result.Err
43
import com.github.michaelbull.result.Ok
54
import com.github.michaelbull.result.Result
5+
import com.github.michaelbull.result.asErr
6+
import com.github.michaelbull.result.binding
67
import kotlinx.coroutines.CancellationException
78
import kotlinx.coroutines.CoroutineScope
89
import kotlinx.coroutines.Job
@@ -12,13 +13,31 @@ import kotlinx.coroutines.sync.Mutex
1213
import kotlinx.coroutines.sync.withLock
1314
import kotlin.contracts.InvocationKind
1415
import kotlin.contracts.contract
15-
import kotlin.coroutines.CoroutineContext
1616

1717
/**
18-
* Suspending variant of [binding][com.github.michaelbull.result.binding].
19-
* The suspendable [block] runs in a new [CoroutineScope], inheriting the parent [CoroutineContext].
20-
* This new scope is [cancelled][CoroutineScope.cancel] once a failing bind is encountered, eagerly cancelling all
21-
* child [jobs][Job].
18+
* Calls the specified function [block] with [CoroutineBindingScope] as its receiver and returns
19+
* its [Result].
20+
*
21+
* When inside a binding [block], the [bind][CoroutineBindingScope.bind] function is accessible on
22+
* any [Result]. Calling the [bind][CoroutineBindingScope.bind] function will attempt to unwrap the
23+
* [Result] and locally return its [value][Result.value].
24+
*
25+
* Unlike [binding], this function is designed for _concurrent decomposition_ of work. When any
26+
* [bind][CoroutineBindingScope.bind] returns an error, the [CoroutineScope] will be
27+
* [cancelled][Job.cancel], cancelling all the other children.
28+
*
29+
* This function returns as soon as the given [block] and all its child coroutines are completed.
30+
*
31+
* Example:
32+
* ```
33+
* suspend fun provideX(): Result<Int, ExampleErr> { ... }
34+
* suspend fun provideY(): Result<Int, ExampleErr> { ... }
35+
*
36+
* val result: Result<Int, ExampleErr> = coroutineBinding {
37+
* val x = async { provideX().bind() }
38+
* val y = async { provideY().bind() }
39+
* x.await() + y.await()
40+
* }
2241
*/
2342
public suspend inline fun <V, E> coroutineBinding(crossinline block: suspend CoroutineBindingScope<E>.() -> V): Result<V, E> {
2443
contract {
@@ -55,11 +74,12 @@ internal class CoroutineBindingScopeImpl<E>(
5574
var result: Result<Nothing, E>? = null
5675

5776
override suspend fun <V> Result<V, E>.bind(): V {
58-
return when (this) {
59-
is Ok -> value
60-
is Err -> mutex.withLock {
77+
return if (isOk) {
78+
value
79+
} else {
80+
mutex.withLock {
6181
if (result == null) {
62-
result = this
82+
result = this.asErr()
6383
coroutineContext.cancel(BindCancellationException)
6484
}
6585

kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/And.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@ import kotlin.contracts.InvocationKind
44
import kotlin.contracts.contract
55

66
/**
7-
* Returns [result] if this [Result] is [Ok], otherwise this [Err].
7+
* Returns [result] if this result [is ok][Result.isOk], otherwise [this].
88
*
99
* - Rust: [Result.and](https://doc.rust-lang.org/std/result/enum.Result.html#method.and)
1010
*/
1111
public infix fun <V, E, U> Result<V, E>.and(result: Result<U, E>): Result<U, E> {
12-
return when (this) {
13-
is Ok -> result
14-
is Err -> this
12+
return when {
13+
isOk -> result
14+
else -> this.asErr()
1515
}
1616
}
1717

1818
/**
1919
* Maps this [Result<V, E>][Result] to [Result<U, E>][Result] by either applying the [transform]
20-
* function if this [Result] is [Ok], or returning this [Err].
20+
* function if this result [is ok][Result.isOk], or returning [this].
2121
*
2222
* - Elm: [Result.andThen](http://package.elm-lang.org/packages/elm-lang/core/latest/Result#andThen)
2323
* - Rust: [Result.and_then](https://doc.rust-lang.org/std/result/enum.Result.html#method.and_then)
@@ -27,8 +27,8 @@ public inline infix fun <V, E, U> Result<V, E>.andThen(transform: (V) -> Result<
2727
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
2828
}
2929

30-
return when (this) {
31-
is Ok -> transform(value)
32-
is Err -> this
30+
return when {
31+
isOk -> transform(value)
32+
else -> this.asErr()
3333
}
3434
}

kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Binding.kt

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ import kotlin.contracts.InvocationKind
44
import kotlin.contracts.contract
55

66
/**
7-
* Calls the specified function [block] with [BindingScope] as its receiver and returns its [Result].
7+
* Calls the specified function [block] with [BindingScope] as its receiver and returns its
8+
* [Result].
89
*
9-
* When inside a [binding] block, the [bind][BindingScope.bind] function is accessible on any [Result]. Calling the
10-
* [bind][BindingScope.bind] function will attempt to unwrap the [Result] and locally return its [value][Ok.value]. If
11-
* the [Result] is an [Err], the binding block will terminate with that bind and return that failed-to-bind [Err].
10+
* When inside a binding [block], the [bind][BindingScope.bind] function is accessible on any
11+
* [Result]. Calling the [bind][BindingScope.bind] function will attempt to unwrap the [Result]
12+
* and locally return its [value][Result.value].
13+
*
14+
* If a [bind][BindingScope.bind] returns an error, the [block] will terminate immediately.
1215
*
1316
* Example:
1417
* ```
@@ -48,12 +51,11 @@ internal class BindingScopeImpl<E> : BindingScope<E> {
4851
var result: Result<Nothing, E>? = null
4952

5053
override fun <V> Result<V, E>.bind(): V {
51-
return when (this) {
52-
is Ok -> value
53-
is Err -> {
54-
this@BindingScopeImpl.result = this
55-
throw BindException
56-
}
54+
return if (isOk) {
55+
value
56+
} else {
57+
result = this.asErr()
58+
throw BindException
5759
}
5860
}
5961
}

kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Get.kt

Lines changed: 44 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -4,75 +4,66 @@ import kotlin.contracts.InvocationKind
44
import kotlin.contracts.contract
55

66
/**
7-
* Returns the [value][Ok.value] if this [Result] is [Ok], otherwise `null`.
7+
* Returns the [value][Result.value] if this result [is ok][Result.isOk], otherwise `null`.
88
*
99
* - Elm: [Result.toMaybe](http://package.elm-lang.org/packages/elm-lang/core/latest/Result#toMaybe)
1010
* - Rust: [Result.ok](https://doc.rust-lang.org/std/result/enum.Result.html#method.ok)
1111
*/
1212
public fun <V, E> Result<V, E>.get(): V? {
13-
contract {
14-
returnsNotNull() implies (this@get is Ok<V>)
15-
returns(null) implies (this@get is Err<E>)
16-
}
17-
18-
return when (this) {
19-
is Ok -> value
20-
is Err -> null
13+
return when {
14+
isOk -> value
15+
else -> null
2116
}
2217
}
2318

2419
/**
25-
* Returns the [error][Err.error] if this [Result] is [Err], otherwise `null`.
20+
* Returns the [error][Result.error] if this result [is an error][Result.isErr], otherwise `null`.
2621
*
2722
* - Rust: [Result.err](https://doc.rust-lang.org/std/result/enum.Result.html#method.err)
2823
*/
2924
public fun <V, E> Result<V, E>.getError(): E? {
30-
contract {
31-
returns(null) implies (this@getError is Ok<V>)
32-
returnsNotNull() implies (this@getError is Err<E>)
33-
}
34-
35-
return when (this) {
36-
is Ok -> null
37-
is Err -> error
25+
return when {
26+
isErr -> error
27+
else -> null
3828
}
3929
}
4030

4131
/**
42-
* Returns the [value][Ok.value] if this [Result] is [Ok], otherwise [default].
32+
* Returns the [value][Result.value] if this result [is ok][Result.isOk], otherwise [default].
4333
*
4434
* - Elm: [Result.withDefault](http://package.elm-lang.org/packages/elm-lang/core/latest/Result#withDefault)
4535
* - Haskell: [Result.fromLeft](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Either.html#v:fromLeft)
4636
* - Rust: [Result.unwrap_or](https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap_or)
4737
*
4838
* @param default The value to return if [Err].
49-
* @return The [value][Ok.value] if [Ok], otherwise [default].
39+
* @return The [value][Result.value] if [Ok], otherwise [default].
5040
*/
5141
public infix fun <V, E> Result<V, E>.getOr(default: V): V {
52-
return when (this) {
53-
is Ok -> value
54-
is Err -> default
42+
return when {
43+
isOk -> value
44+
else -> default
5545
}
5646
}
5747

5848
/**
59-
* Returns the [error][Err.error] if this [Result] is [Err], otherwise [default].
49+
* Returns the [error][Result.error] if this result [is an error][Result.isErr], otherwise
50+
* [default].
6051
*
6152
* - Haskell: [Result.fromRight](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Either.html#v:fromRight)
6253
*
6354
* @param default The error to return if [Ok].
64-
* @return The [error][Err.error] if [Err], otherwise [default].
55+
* @return The [error][Result.error] if [Err], otherwise [default].
6556
*/
6657
public infix fun <V, E> Result<V, E>.getErrorOr(default: E): E {
67-
return when (this) {
68-
is Ok -> default
69-
is Err -> error
58+
return when {
59+
isOk -> default
60+
else -> error
7061
}
7162
}
7263

7364
/**
74-
* Returns the [value][Ok.value] if this [Result] is [Ok], otherwise the
75-
* [transformation][transform] of the [error][Err.error].
65+
* Returns the [value][Result.value] if this result [is ok][Result.isOk], otherwise the
66+
* [transformation][transform] of the [error][Result.error].
7667
*
7768
* - Elm: [Result.extract](http://package.elm-lang.org/packages/elm-community/result-extra/2.2.0/Result-Extra#extract)
7869
* - Rust: [Result.unwrap_or_else](https://doc.rust-lang.org/src/core/result.rs.html#735-740)
@@ -82,69 +73,64 @@ public inline infix fun <V, E> Result<V, E>.getOrElse(transform: (E) -> V): V {
8273
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
8374
}
8475

85-
return when (this) {
86-
is Ok -> value
87-
is Err -> transform(error)
76+
return when {
77+
isOk -> value
78+
else -> transform(error)
8879
}
8980
}
9081

9182
/**
92-
* Returns the [error][Err.error] if this [Result] is [Err], otherwise the
93-
* [transformation][transform] of the [value][Ok.value].
83+
* Returns the [error][Result.error] if this result [is an error][Result.isErr], otherwise the
84+
* [transformation][transform] of the [value][Result.value].
9485
*/
9586
public inline infix fun <V, E> Result<V, E>.getErrorOrElse(transform: (V) -> E): E {
9687
contract {
9788
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
9889
}
9990

100-
return when (this) {
101-
is Ok -> transform(value)
102-
is Err -> error
91+
return when {
92+
isErr -> error
93+
else -> transform(value)
10394
}
10495
}
10596

10697
/**
107-
* Returns the [value][Ok.value] if this [Result] is [Ok], otherwise throws the
108-
* [error][Err.error].
98+
* Returns the [value][Result.value] if this result [is ok][Result.isOk], otherwise throws the
99+
* [error][Result.error].
109100
*
110101
* This is functionally equivalent to [`getOrElse { throw it }`][getOrElse].
111102
*/
112103
public fun <V, E : Throwable> Result<V, E>.getOrThrow(): V {
113-
contract {
114-
returns() implies (this@getOrThrow is Ok<V>)
115-
}
116-
117-
return when (this) {
118-
is Ok -> value
119-
is Err -> throw error
104+
return when {
105+
isOk -> value
106+
else -> throw error
120107
}
121108
}
122109

123110
/**
124-
* Returns the [value][Ok.value] if this [Result] is [Ok], otherwise throws the
125-
* [transformation][transform] of the [error][Err.error] to a [Throwable].
111+
* Returns the [value][Result.value] if this result [is ok][Result.isOk], otherwise throws the
112+
* [transformation][transform] of the [error][Result.error] to a [Throwable].
126113
*/
127114
public inline infix fun <V, E> Result<V, E>.getOrThrow(transform: (E) -> Throwable): V {
128115
contract {
129-
returns() implies (this@getOrThrow is Ok<V>)
130116
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
131117
}
132118

133-
return when (this) {
134-
is Ok -> value
135-
is Err -> throw transform(error)
119+
return when {
120+
isOk -> value
121+
else -> throw transform(error)
136122
}
137123
}
138124

139125
/**
140-
* Merges this [Result<V, E>][Result] to [U], returning the [value][Ok.value] if this [Result] is [Ok], otherwise the
141-
* [error][Err.error].
126+
* Merges this [Result<V, E>][Result] to [U], returning the [value][Result.value] if this result
127+
* [is ok][Result.isOk], otherwise the [error][Result.error].
142128
*
143129
* - Scala: [MergeableEither.merge](https://www.scala-lang.org/api/2.12.0/scala/util/Either$$MergeableEither.html#merge:A)
144130
*/
145131
public fun <V : U, E : U, U> Result<V, E>.merge(): U {
146-
return when (this) {
147-
is Ok -> value
148-
is Err -> error
132+
return when {
133+
isOk -> value
134+
else -> error
149135
}
150136
}

0 commit comments

Comments
 (0)