Deprecate Ok/Err as return types

This is in preparation for the v2 release where these don't exist as types.
This commit is contained in:
Michael Bull 2024-03-16 19:02:01 +00:00
parent b19894a08c
commit 7ce7c16d7f
13 changed files with 533 additions and 419 deletions

View File

@ -1,8 +1,9 @@
package com.github.michaelbull.result.coroutines
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.asErr
import com.github.michaelbull.result.binding
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@ -12,13 +13,31 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.coroutines.CoroutineContext
/**
* Suspending variant of [binding][com.github.michaelbull.result.binding].
* The suspendable [block] runs in a new [CoroutineScope], inheriting the parent [CoroutineContext].
* This new scope is [cancelled][CoroutineScope.cancel] once a failing bind is encountered, eagerly cancelling all
* child [jobs][Job].
* Calls the specified function [block] with [CoroutineBindingScope] as its receiver and returns
* its [Result].
*
* When inside a binding [block], the [bind][CoroutineBindingScope.bind] function is accessible on
* any [Result]. Calling the [bind][CoroutineBindingScope.bind] function will attempt to unwrap the
* [Result] and locally return its [value][Result.value].
*
* Unlike [binding], this function is designed for _concurrent decomposition_ of work. When any
* [bind][CoroutineBindingScope.bind] returns an error, the [CoroutineScope] will be
* [cancelled][Job.cancel], cancelling all the other children.
*
* This function returns as soon as the given [block] and all its child coroutines are completed.
*
* Example:
* ```
* suspend fun provideX(): Result<Int, ExampleErr> { ... }
* suspend fun provideY(): Result<Int, ExampleErr> { ... }
*
* val result: Result<Int, ExampleErr> = coroutineBinding {
* val x = async { provideX().bind() }
* val y = async { provideY().bind() }
* x.await() + y.await()
* }
*/
public suspend inline fun <V, E> coroutineBinding(crossinline block: suspend CoroutineBindingScope<E>.() -> V): Result<V, E> {
contract {
@ -55,11 +74,12 @@ internal class CoroutineBindingScopeImpl<E>(
var result: Result<Nothing, E>? = null
override suspend fun <V> Result<V, E>.bind(): V {
return when (this) {
is Ok -> value
is Err -> mutex.withLock {
return if (isOk) {
value
} else {
mutex.withLock {
if (result == null) {
result = this
result = this.asErr()
coroutineContext.cancel(BindCancellationException)
}

View File

@ -4,14 +4,14 @@ import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/**
* Returns [result] if this [Result] is [Ok], otherwise this [Err].
* Returns [result] if this result [is ok][Result.isOk], otherwise [this].
*
* - Rust: [Result.and](https://doc.rust-lang.org/std/result/enum.Result.html#method.and)
*/
public infix fun <V, E, U> Result<V, E>.and(result: Result<U, E>): Result<U, E> {
return when (this) {
is Ok -> result
is Err -> this
return when {
isOk -> result
else -> this.asErr()
}
}
@ -26,7 +26,7 @@ public inline infix fun <V, E, U> Result<V, E>.and(result: () -> Result<U, E>):
/**
* Maps this [Result<V, E>][Result] to [Result<U, E>][Result] by either applying the [transform]
* function if this [Result] is [Ok], or returning this [Err].
* function if this result [is ok][Result.isOk], or returning [this].
*
* - Elm: [Result.andThen](http://package.elm-lang.org/packages/elm-lang/core/latest/Result#andThen)
* - Rust: [Result.and_then](https://doc.rust-lang.org/std/result/enum.Result.html#method.and_then)
@ -36,8 +36,8 @@ public inline infix fun <V, E, U> Result<V, E>.andThen(transform: (V) -> Result<
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> transform(value)
is Err -> this
return when {
isOk -> transform(value)
else -> this.asErr()
}
}

View File

@ -4,11 +4,14 @@ import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/**
* Calls the specified function [block] with [BindingScope] as its receiver and returns its [Result].
* Calls the specified function [block] with [BindingScope] as its receiver and returns its
* [Result].
*
* When inside a [binding] block, the [bind][BindingScope.bind] function is accessible on any [Result]. Calling the
* [bind][BindingScope.bind] function will attempt to unwrap the [Result] and locally return its [value][Ok.value]. If
* the [Result] is an [Err], the binding block will terminate with that bind and return that failed-to-bind [Err].
* When inside a binding [block], the [bind][BindingScope.bind] function is accessible on any
* [Result]. Calling the [bind][BindingScope.bind] function will attempt to unwrap the [Result]
* and locally return its [value][Result.value].
*
* If a [bind][BindingScope.bind] returns an error, the [block] will terminate immediately.
*
* Example:
* ```
@ -54,12 +57,11 @@ internal class BindingScopeImpl<E> : BindingScope<E> {
var result: Result<Nothing, E>? = null
override fun <V> Result<V, E>.bind(): V {
return when (this) {
is Ok -> value
is Err -> {
this@BindingScopeImpl.result = this
throw BindException
}
return if (isOk) {
value
} else {
result = this.asErr()
throw BindException
}
}
}

View File

@ -4,7 +4,7 @@ import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/**
* Returns the [value][Ok.value] if this [Result] is [Ok], otherwise `null`.
* Returns the [value][Result.value] if this result [is ok][Result.isOk], otherwise `null`.
*
* - Elm: [Result.toMaybe](http://package.elm-lang.org/packages/elm-lang/core/latest/Result#toMaybe)
* - Rust: [Result.ok](https://doc.rust-lang.org/std/result/enum.Result.html#method.ok)
@ -15,14 +15,14 @@ public fun <V, E> Result<V, E>.get(): V? {
returns(null) implies (this@get is Err<E>)
}
return when (this) {
is Ok -> value
is Err -> null
return when {
isOk -> value
else -> null
}
}
/**
* Returns the [error][Err.error] if this [Result] is [Err], otherwise `null`.
* Returns the [error][Result.error] if this result [is an error][Result.isErr], otherwise `null`.
*
* - Rust: [Result.err](https://doc.rust-lang.org/std/result/enum.Result.html#method.err)
*/
@ -32,26 +32,26 @@ public fun <V, E> Result<V, E>.getError(): E? {
returnsNotNull() implies (this@getError is Err<E>)
}
return when (this) {
is Ok -> null
is Err -> error
return when {
isErr -> error
else -> null
}
}
/**
* Returns the [value][Ok.value] if this [Result] is [Ok], otherwise [default].
* Returns the [value][Result.value] if this result [is ok][Result.isOk], otherwise [default].
*
* - Elm: [Result.withDefault](http://package.elm-lang.org/packages/elm-lang/core/latest/Result#withDefault)
* - Haskell: [Result.fromLeft](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Either.html#v:fromLeft)
* - Rust: [Result.unwrap_or](https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap_or)
*
* @param default The value to return if [Err].
* @return The [value][Ok.value] if [Ok], otherwise [default].
* @return The [value][Result.value] if [Ok], otherwise [default].
*/
public infix fun <V, E> Result<V, E>.getOr(default: V): V {
return when (this) {
is Ok -> value
is Err -> default
return when {
isOk -> value
else -> default
}
}
@ -65,17 +65,18 @@ public inline infix fun <V, E> Result<V, E>.getOr(default: () -> V): V {
}
/**
* Returns the [error][Err.error] if this [Result] is [Err], otherwise [default].
* Returns the [error][Result.error] if this result [is an error][Result.isErr], otherwise
* [default].
*
* - Haskell: [Result.fromRight](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Either.html#v:fromRight)
*
* @param default The error to return if [Ok].
* @return The [error][Err.error] if [Err], otherwise [default].
* @return The [error][Result.error] if [Err], otherwise [default].
*/
public infix fun <V, E> Result<V, E>.getErrorOr(default: E): E {
return when (this) {
is Ok -> default
is Err -> error
return when {
isOk -> default
else -> error
}
}
@ -89,8 +90,8 @@ public inline infix fun <V, E> Result<V, E>.getErrorOr(default: () -> E): E {
}
/**
* Returns the [value][Ok.value] if this [Result] is [Ok], otherwise the
* [transformation][transform] of the [error][Err.error].
* Returns the [value][Result.value] if this result [is ok][Result.isOk], otherwise the
* [transformation][transform] of the [error][Result.error].
*
* - Elm: [Result.extract](http://package.elm-lang.org/packages/elm-community/result-extra/2.2.0/Result-Extra#extract)
* - Rust: [Result.unwrap_or_else](https://doc.rust-lang.org/src/core/result.rs.html#735-740)
@ -100,30 +101,30 @@ public inline infix fun <V, E> Result<V, E>.getOrElse(transform: (E) -> V): V {
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> value
is Err -> transform(error)
return when {
isOk -> value
else -> transform(error)
}
}
/**
* Returns the [error][Err.error] if this [Result] is [Err], otherwise the
* [transformation][transform] of the [value][Ok.value].
* Returns the [error][Result.error] if this result [is an error][Result.isErr], otherwise the
* [transformation][transform] of the [value][Result.value].
*/
public inline infix fun <V, E> Result<V, E>.getErrorOrElse(transform: (V) -> E): E {
contract {
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> transform(value)
is Err -> error
return when {
isErr -> error
else -> transform(value)
}
}
/**
* Returns the [value][Ok.value] if this [Result] is [Ok], otherwise throws the
* [error][Err.error].
* Returns the [value][Result.value] if this result [is ok][Result.isOk], otherwise throws the
* [error][Result.error].
*
* This is functionally equivalent to [`getOrElse { throw it }`][getOrElse].
*/
@ -132,15 +133,15 @@ public fun <V, E : Throwable> Result<V, E>.getOrThrow(): V {
returns() implies (this@getOrThrow is Ok<V>)
}
return when (this) {
is Ok -> value
is Err -> throw error
return when {
isOk -> value
else -> throw error
}
}
/**
* Returns the [value][Ok.value] if this [Result] is [Ok], otherwise throws the
* [transformation][transform] of the [error][Err.error] to a [Throwable].
* Returns the [value][Result.value] if this result [is ok][Result.isOk], otherwise throws the
* [transformation][transform] of the [error][Result.error] to a [Throwable].
*/
public inline infix fun <V, E> Result<V, E>.getOrThrow(transform: (E) -> Throwable): V {
contract {
@ -148,21 +149,21 @@ public inline infix fun <V, E> Result<V, E>.getOrThrow(transform: (E) -> Throwab
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> value
is Err -> throw transform(error)
return when {
isOk -> value
else -> throw transform(error)
}
}
/**
* Merges this [Result<V, E>][Result] to [U], returning the [value][Ok.value] if this [Result] is [Ok], otherwise the
* [error][Err.error].
* Merges this [Result<V, E>][Result] to [U], returning the [value][Result.value] if this result
* [is ok][Result.isOk], otherwise the [error][Result.error].
*
* - Scala: [MergeableEither.merge](https://www.scala-lang.org/api/2.12.0/scala/util/Either$$MergeableEither.html#merge:A)
*/
public fun <V : U, E : U, U> Result<V, E>.merge(): U {
return when (this) {
is Ok -> value
is Err -> error
return when {
isOk -> value
else -> error
}
}

View File

@ -1,25 +1,26 @@
package com.github.michaelbull.result
/**
* Returns a list containing only elements that are [Ok].
* Returns a list containing only elements that [are ok][Result.isOk].
*/
public fun <V, E> Iterable<Result<V, E>>.filterValues(): List<V> {
return filterValuesTo(ArrayList())
}
/**
* Returns a list containing only elements that are [Err].
* Returns a list containing only elements that [are an error][Result.isErr].
*/
public fun <V, E> Iterable<Result<V, E>>.filterErrors(): List<E> {
return filterErrorsTo(ArrayList())
}
/**
* Appends the [values][Ok.value] of each element that is [Ok] to the given [destination].
* Appends the [values][Result.value] of each element that [is ok][Result.isOk] to the given
* [destination].
*/
public fun <V, E, C : MutableCollection<in V>> Iterable<Result<V, E>>.filterValuesTo(destination: C): C {
for (element in this) {
if (element is Ok<V>) {
if (element.isOk) {
destination.add(element.value)
}
}
@ -28,11 +29,12 @@ public fun <V, E, C : MutableCollection<in V>> Iterable<Result<V, E>>.filterValu
}
/**
* Appends the [errors][Err.error] of each element that is [Err] to the given [destination].
* Appends the [errors][Result.error] of each element that [is an error][Result.isErr] to the given
* [destination].
*/
public fun <V, E, C : MutableCollection<in E>> Iterable<Result<V, E>>.filterErrorsTo(destination: C): C {
for (element in this) {
if (element is Err<E>) {
if (element.isErr) {
destination.add(element.error)
}
}
@ -41,45 +43,45 @@ public fun <V, E, C : MutableCollection<in E>> Iterable<Result<V, E>>.filterErro
}
/**
* Returns `true` if each element is [Ok], `false` otherwise.
* Returns `true` if each element [is ok][Result.isOk], `false` otherwise.
*/
public fun <V, E> Iterable<Result<V, E>>.allOk(): Boolean {
return all { it is Ok }
return all(Result<V, E>::isOk)
}
/**
* Returns `true` if each element is [Err], `false` otherwise.
* Returns `true` if each element [is an error][Result.isErr], `false` otherwise.
*/
public fun <V, E> Iterable<Result<V, E>>.allErr(): Boolean {
return all { it is Err }
return all(Result<V, E>::isErr)
}
/**
* Returns `true` if at least one element is [Ok], `false` otherwise.
* Returns `true` if at least one element [is ok][Result.isOk], `false` otherwise.
*/
public fun <V, E> Iterable<Result<V, E>>.anyOk(): Boolean {
return any { it is Ok }
return any(Result<V, E>::isOk)
}
/**
* Returns `true` if at least one element is [Err], `false` otherwise.
* Returns `true` if at least one element [is an error][Result.isErr], `false` otherwise.
*/
public fun <V, E> Iterable<Result<V, E>>.anyErr(): Boolean {
return any { it is Err }
return any(Result<V, E>::isErr)
}
/**
* Returns the number of elements that are [Ok].
* Returns the number of elements that [are ok][Result.isOk].
*/
public fun <V, E> Iterable<Result<V, E>>.countOk(): Int {
return count { it is Ok }
return count(Result<V, E>::isOk)
}
/**
* Returns the number of elements that are [Err].
* Returns the number of elements that [are an error][Result.isErr].
*/
public fun <V, E> Iterable<Result<V, E>>.countErr(): Int {
return count { it is Err }
return count(Result<V, E>::isErr)
}
/**
@ -93,9 +95,11 @@ public inline fun <T, R, E> Iterable<T>.fold(
var accumulator = initial
for (element in this) {
accumulator = when (val result = operation(accumulator, element)) {
is Ok -> result.value
is Err -> return Err(result.error)
val result = operation(accumulator, element)
accumulator = when {
result.isOk -> result.value
else -> return Err(result.error)
}
}
@ -106,7 +110,7 @@ public inline fun <T, R, E> Iterable<T>.fold(
* Accumulates value starting with [initial] value and applying [operation] from right to left to
* each element and current accumulator value.
*/
public inline fun <T, R, E, C : Result<R, E>> List<T>.foldRight(
public inline fun <T, R, E> List<T>.foldRight(
initial: R,
operation: (T, acc: R) -> Result<R, E>,
): Result<R, E> {
@ -116,9 +120,11 @@ public inline fun <T, R, E, C : Result<R, E>> List<T>.foldRight(
val iterator = listIterator(size)
while (iterator.hasPrevious()) {
accumulator = when (val result = operation(iterator.previous(), accumulator)) {
is Ok -> result.value
is Err -> return Err(result.error)
val result = operation(iterator.previous(), accumulator)
accumulator = when {
result.isOk -> result.value
else -> return Err(result.error)
}
}
}
@ -127,8 +133,8 @@ public inline fun <T, R, E, C : Result<R, E>> List<T>.foldRight(
}
/**
* Combines a vararg of [Results][Result] into a single [Result] (holding a [List]). Elements in the returned list
* are in the same order is the input vararg.
* Combines the specified [results] into a single [Result] (holding a [List]). Elements in the
* returned list are in the same order as the specified [results].
*
* - Elm: [Result.Extra.combine](http://package.elm-lang.org/packages/elm-community/result-extra/2.2.0/Result-Extra#combine)
*/
@ -137,16 +143,16 @@ public fun <V, E, R : Result<V, E>> combine(vararg results: R): Result<List<V>,
}
/**
* Combines an [Iterable] of [Results][Result] into a single [Result] (holding a [List]). Elements in the returned
* list are in the input [Iterable] order.
* Combines [this] iterable into a single [Result] (holding a [List]). Elements in the returned
* list are in the the same order as [this].
*
* - Elm: [Result.Extra.combine](http://package.elm-lang.org/packages/elm-community/result-extra/2.2.0/Result-Extra#combine)
*/
public fun <V, E> Iterable<Result<V, E>>.combine(): Result<List<V>, E> {
val values = map {
when (it) {
is Ok -> it.value
is Err -> return it
val values = map { result ->
when {
result.isOk -> result.value
else -> return result.asErr()
}
}
@ -154,8 +160,9 @@ public fun <V, E> Iterable<Result<V, E>>.combine(): Result<List<V>, E> {
}
/**
* Extracts from a vararg of [Results][Result] all the [Ok] elements. All the [Ok] elements are
* extracted in order.
* Returns a [List] containing the [value][Result.value] of each element in the specified [results]
* that [is ok][Result.isOk]. Elements in the returned list are in the same order as the specified
* [results].
*
* - Haskell: [Data.Either.lefts](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Either.html#v:lefts)
*/
@ -178,8 +185,9 @@ public fun <V, E> Iterable<Result<V, E>>.getAll(): List<V> {
}
/**
* Extracts from a vararg of [Results][Result] all the [Err] elements. All the [Err] elements are
* extracted in order.
* Returns a [List] containing the [error][Result.error] of each element in the specified [results]
* that [is an error][Result.isErr]. Elements in the returned list are in the same order as the
* specified [results].
*
* - Haskell: [Data.Either.rights](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Either.html#v:rights)
*/
@ -202,9 +210,9 @@ public fun <V, E> Iterable<Result<V, E>>.getAllErrors(): List<E> {
}
/**
* Partitions a vararg of [Results][Result] into a [Pair] of [Lists][List]. All the [Ok] elements
* are extracted, in order, to the [first][Pair.first] value. Similarly the [Err] elements are
* extracted to the [Pair.second] value.
* Partitions the specified [results] into a [Pair] of [Lists][List]. An element that
* [is ok][Result.isOk] will appear in the [first][Pair.first] list, whereas an element that
* [is an error][Result.isErr] will appear in the [second][Pair.second] list.
*
* - Haskell: [Data.Either.partitionEithers](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Either.html#v:partitionEithers)
*/
@ -213,9 +221,10 @@ public fun <V, E, R : Result<V, E>> partition(vararg results: R): Pair<List<V>,
}
/**
* Partitions an [Iterable] of [Results][Result] into a [Pair] of [Lists][List]. All the [Ok]
* elements are extracted, in order, to the [first][Pair.first] value. Similarly the [Err] elements
* are extracted to the [Pair.second] value.
*
* Partitions this into a [Pair] of [Lists][List]. An element that [is ok][Result.isOk] will appear
* in the [first][Pair.first] list, whereas an element that [is an error][Result.isErr] will appear
* in the [second][Pair.second] list.
*
* - Haskell: [Data.Either.partitionEithers](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Either.html#v:partitionEithers)
*/
@ -223,10 +232,11 @@ public fun <V, E> Iterable<Result<V, E>>.partition(): Pair<List<V>, List<E>> {
val values = mutableListOf<V>()
val errors = mutableListOf<E>()
forEach { result ->
when (result) {
is Ok -> values.add(result.value)
is Err -> errors.add(result.error)
for (result in this) {
if (result.isOk) {
values += result.value
} else {
errors += result.error
}
}
@ -236,15 +246,17 @@ public fun <V, E> Iterable<Result<V, E>>.partition(): Pair<List<V>, List<E>> {
/**
* Returns a [Result<List<U>, E>][Result] containing the results of applying the given [transform]
* function to each element in the original collection, returning early with the first [Err] if a
* transformation fails. Elements in the returned list are in the input [Iterable] order.
* transformation fails. Elements in the returned list are in the same order as [this].
*/
public inline fun <V, E, U> Iterable<V>.mapResult(
transform: (V) -> Result<U, E>,
): Result<List<U>, E> {
val values = map { element ->
when (val transformed = transform(element)) {
is Ok -> transformed.value
is Err -> return transformed
val transformed = transform(element)
when {
transformed.isOk -> transformed.value
else -> return transformed.asErr()
}
}
@ -254,16 +266,18 @@ public inline fun <V, E, U> Iterable<V>.mapResult(
/**
* Applies the given [transform] function to each element of the original collection and appends
* the results to the given [destination], returning early with the first [Err] if a
* transformation fails. Elements in the returned list are in the input [Iterable] order.
* transformation fails. Elements in the returned list are in the same order as [this].
*/
public inline fun <V, E, U, C : MutableCollection<in U>> Iterable<V>.mapResultTo(
destination: C,
transform: (V) -> Result<U, E>,
): Result<C, E> {
val values = mapTo(destination) { element ->
when (val transformed = transform(element)) {
is Ok -> transformed.value
is Err -> return transformed
val transformed = transform(element)
when {
transformed.isOk -> transformed.value
else -> return transformed.asErr()
}
}
@ -273,17 +287,19 @@ public inline fun <V, E, U, C : MutableCollection<in U>> Iterable<V>.mapResultTo
/**
* Returns a [Result<List<U>, E>][Result] containing only the non-null results of applying the
* given [transform] function to each element in the original collection, returning early with the
* first [Err] if a transformation fails. Elements in the returned list are in the input [Iterable]
* order.
* first [Err] if a transformation fails. Elements in the returned list are in the same order as
* [this].
*/
public inline fun <V, E, U : Any> Iterable<V>.mapResultNotNull(
transform: (V) -> Result<U, E>?,
): Result<List<U>, E> {
val values = mapNotNull { element ->
when (val transformed = transform(element)) {
is Ok -> transformed.value
is Err -> return transformed
null -> null
val transformed = transform(element)
when {
transformed == null -> null
transformed.isOk -> transformed.value
else -> return transformed.asErr()
}
}
@ -300,10 +316,12 @@ public inline fun <V, E, U : Any, C : MutableCollection<in U>> Iterable<V>.mapRe
transform: (V) -> Result<U, E>?,
): Result<C, E> {
val values = mapNotNullTo(destination) { element ->
when (val transformed = transform(element)) {
is Ok -> transformed.value
is Err -> return transformed
null -> null
val transformed = transform(element)
when {
transformed == null -> null
transformed.isOk -> transformed.value
else -> return transformed.asErr()
}
}
@ -313,16 +331,18 @@ public inline fun <V, E, U : Any, C : MutableCollection<in U>> Iterable<V>.mapRe
/**
* Returns a [Result<List<U>, E>][Result] containing the results of applying the given [transform]
* function to each element and its index in the original collection, returning early with the
* first [Err] if a transformation fails. Elements in the returned list are in the input [Iterable]
* order.
* first [Err] if a transformation fails. Elements in the returned list are in same order as
* [this].
*/
public inline fun <V, E, U> Iterable<V>.mapResultIndexed(
transform: (index: Int, V) -> Result<U, E>,
): Result<List<U>, E> {
val values = mapIndexed { index, element ->
when (val transformed = transform(index, element)) {
is Ok -> transformed.value
is Err -> return transformed
val transformed = transform(index, element)
when {
transformed.isOk -> transformed.value
else -> return transformed.asErr()
}
}
@ -339,9 +359,11 @@ public inline fun <V, E, U, C : MutableCollection<in U>> Iterable<V>.mapResultIn
transform: (index: Int, V) -> Result<U, E>,
): Result<C, E> {
val values = mapIndexedTo(destination) { index, element ->
when (val transformed = transform(index, element)) {
is Ok -> transformed.value
is Err -> return transformed
val transformed = transform(index, element)
when {
transformed.isOk -> transformed.value
else -> return transformed.asErr()
}
}
@ -352,16 +374,18 @@ public inline fun <V, E, U, C : MutableCollection<in U>> Iterable<V>.mapResultIn
* Returns a [Result<List<U>, E>][Result] containing only the non-null results of applying the
* given [transform] function to each element and its index in the original collection, returning
* early with the first [Err] if a transformation fails. Elements in the returned list are in
* the input [Iterable] order.
* the same order as [this].
*/
public inline fun <V, E, U : Any> Iterable<V>.mapResultIndexedNotNull(
transform: (index: Int, V) -> Result<U, E>?,
): Result<List<U>, E> {
val values = mapIndexedNotNull { index, element ->
when (val transformed = transform(index, element)) {
is Ok -> transformed.value
is Err -> return transformed
null -> null
val transformed = transform(index, element)
when {
transformed == null -> null
transformed.isOk -> transformed.value
else -> return transformed.asErr()
}
}
@ -378,10 +402,12 @@ public inline fun <V, E, U : Any, C : MutableCollection<in U>> Iterable<V>.mapRe
transform: (index: Int, V) -> Result<U, E>?,
): Result<C, E> {
val values = mapIndexedNotNullTo(destination) { index, element ->
when (val transformed = transform(index, element)) {
is Ok -> transformed.value
is Err -> return transformed
null -> null
val transformed = transform(index, element)
when {
transformed == null -> null
transformed.isOk -> transformed.value
else -> return transformed.asErr()
}
}

View File

@ -5,7 +5,7 @@ import kotlin.contracts.contract
/**
* Maps this [Result<V, E>][Result] to [Result<U, E>][Result] by either applying the [transform]
* function to the [value][Ok.value] if this [Result] is [Ok], or returning this [Err].
* function to the [value][Result.value] if this result [is ok][Result.isOk], or returning [this].
*
* - Elm: [Result.map](http://package.elm-lang.org/packages/elm-lang/core/latest/Result#map)
* - Haskell: [Data.Bifunctor.first](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Bifunctor.html#v:first)
@ -16,19 +16,19 @@ public inline infix fun <V, E, U> Result<V, E>.map(transform: (V) -> U): Result<
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> Ok(transform(value))
is Err -> this
return when {
isOk -> Ok(transform(value))
else -> this.asErr()
}
}
/**
* Maps this [Result<V, Throwable>][Result] to [Result<U, Throwable>][Result] by either applying
* the [transform] function to the [value][Ok.value] if this [Result] is [Ok], or returning this
* [Err].
* the [transform] function to the [value][Result.value] if this result [is ok][Result.isOk], or
* returning [this].
*
* This function catches any [Throwable] exception thrown by [transform] function and encapsulates
* it as a failure.
* it as an [Err].
*
* - Elm: [Result.map](http://package.elm-lang.org/packages/elm-lang/core/latest/Result#map)
* - Haskell: [Data.Bifunctor.first](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Bifunctor.html#v:first)
@ -39,9 +39,9 @@ public inline infix fun <V, U> Result<V, Throwable>.mapCatching(transform: (V) -
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> runCatching { transform(value) }
is Err -> this
return when {
isOk -> runCatching { transform(value) }
else -> this.asErr()
}
}
@ -53,14 +53,10 @@ public inline infix fun <V, U> Result<V, Throwable>.mapCatching(transform: (V) -
* - Rust: [Result.transpose][https://doc.rust-lang.org/std/result/enum.Result.html#method.transpose]
*/
public inline fun <V, E> Result<V?, E>.transpose(): Result<V, E>? {
return when (this) {
is Ok -> if (value == null) {
null
} else {
Ok(value)
}
is Err -> this
return when {
isOk && value == null -> null
isOk && value != null -> this.asOk()
else -> this.asErr()
}
}
@ -69,16 +65,16 @@ public inline fun <V, E> Result<V?, E>.transpose(): Result<V, E>? {
*
* - Rust: [Result.flatten](https://doc.rust-lang.org/std/result/enum.Result.html#method.flatten)
*/
public inline fun <V, E> Result<Result<V, E>, E>.flatten(): Result<V, E> {
return when (this) {
is Ok -> value
is Err -> this
public fun <V, E> Result<Result<V, E>, E>.flatten(): Result<V, E> {
return when {
isOk -> value
else -> this.asErr()
}
}
/**
* Maps this [Result<V, E>][Result] to [Result<U, E>][Result] by either applying the [transform]
* function if this [Result] is [Ok], or returning this [Err].
* function if this result [is ok][Result.isOk], or returning [this].
*
* This is functionally equivalent to [andThen].
*
@ -94,7 +90,8 @@ public inline infix fun <V, E, U> Result<V, E>.flatMap(transform: (V) -> Result<
/**
* Maps this [Result<V, E>][Result] to [U] by applying either the [success] function if this
* [Result] is [Ok], or the [failure] function if this [Result] is an [Err].
* result [is ok][Result.isOk], or the [failure] function if this result
* [is an error][Result.isErr].
*
* Unlike [mapEither], [success] and [failure] must both return [U].
*
@ -110,15 +107,16 @@ public inline fun <V, E, U> Result<V, E>.mapBoth(
callsInPlace(failure, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> success(value)
is Err -> failure(error)
return when {
isOk -> success(value)
else -> failure(error)
}
}
/**
* Maps this [Result<V, E>][Result] to [U] by applying either the [success] function if this
* [Result] is [Ok], or the [failure] function if this [Result] is an [Err].
* result [is ok][Result.isOk], or the [failure] function if this result
* [is an error][Result.isErr].
*
* Unlike [mapEither], [success] and [failure] must both return [U].
*
@ -141,7 +139,8 @@ public inline fun <V, E, U> Result<V, E>.fold(
/**
* Maps this [Result<V, E>][Result] to [Result<U, E>][Result] by applying either the [success]
* function if this [Result] is [Ok], or the [failure] function if this [Result] is an [Err].
* function if this result [is ok][Result.isOk], or the [failure] function if this result
* [is an error][Result.isErr].
*
* Unlike [mapEither], [success] and [failure] must both return [U].
*
@ -157,15 +156,16 @@ public inline fun <V, E, U> Result<V, E>.flatMapBoth(
callsInPlace(failure, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> success(value)
is Err -> failure(error)
return when {
isOk -> success(value)
else -> failure(error)
}
}
/**
* Maps this [Result<V, E>][Result] to [Result<U, F>][Result] by applying either the [success]
* function if this [Result] is [Ok], or the [failure] function if this [Result] is an [Err].
* function if this result [is ok][Result.isOk], or the [failure] function if this result
* [is an error][Result.isErr].
*
* Unlike [mapBoth], [success] and [failure] may either return [U] or [F] respectively.
*
@ -180,15 +180,16 @@ public inline fun <V, E, U, F> Result<V, E>.mapEither(
callsInPlace(failure, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> Ok(success(value))
is Err -> Err(failure(error))
return when {
isOk -> Ok(success(value))
else -> Err(failure(error))
}
}
/**
* Maps this [Result<V, E>][Result] to [Result<U, F>][Result] by applying either the [success]
* function if this [Result] is [Ok], or the [failure] function if this [Result] is an [Err].
* function if this result [is ok][Result.isOk], or the [failure] function if this result
* [is an error][Result.isErr].
*
* Unlike [mapBoth], [success] and [failure] may either return [U] or [F] respectively.
*
@ -203,15 +204,16 @@ public inline fun <V, E, U, F> Result<V, E>.flatMapEither(
callsInPlace(failure, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> success(value)
is Err -> failure(error)
return when {
isOk -> success(value)
else -> failure(error)
}
}
/**
* Maps this [Result<V, E>][Result] to [Result<V, F>][Result] by either applying the [transform]
* function to the [error][Err.error] if this [Result] is [Err], or returning this [Ok].
* function to the [error][Result.error] if this result [is an error][Result.isErr], or returning
* [this].
*
* - Elm: [Result.mapError](http://package.elm-lang.org/packages/elm-lang/core/latest/Result#mapError)
* - Haskell: [Data.Bifunctor.right](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Bifunctor.html#v:second)
@ -222,46 +224,52 @@ public inline infix fun <V, E, F> Result<V, E>.mapError(transform: (E) -> F): Re
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> this
is Err -> Err(transform(error))
return when {
isErr -> Err(transform(error))
else -> this.asOk()
}
}
/**
* Maps this [Result<V, E>][Result] to [U] by either applying the [transform] function to the
* [value][Ok.value] if this [Result] is [Ok], or returning the [default] if this [Result] is an
* [Err].
* [value][Result.value] if this result [is ok][Result.isOk], or returning the [default] if this
* result [is an error][Result.isErr].
*
* - Rust: [Result.map_or](https://doc.rust-lang.org/std/result/enum.Result.html#method.map_or)
*/
public inline fun <V, E, U> Result<V, E>.mapOr(default: U, transform: (V) -> U): U {
public inline fun <V, E, U> Result<V, E>.mapOr(
default: U,
transform: (V) -> U,
): U {
contract {
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> transform(value)
is Err -> default
return when {
isOk -> transform(value)
else -> default
}
}
/**
* Maps this [Result<V, E>][Result] to [U] by applying either the [transform] function if this
* [Result] is [Ok], or the [default] function if this [Result] is an [Err]. Both of these
* functions must return the same type ([U]).
* result [is ok][Result.isOk], or the [default] function if this result
* [is an error][Result.isErr]. Both of these functions must return the same type ([U]).
*
* - Rust: [Result.map_or_else](https://doc.rust-lang.org/std/result/enum.Result.html#method.map_or_else)
*/
public inline fun <V, E, U> Result<V, E>.mapOrElse(default: (E) -> U, transform: (V) -> U): U {
public inline fun <V, E, U> Result<V, E>.mapOrElse(
default: (E) -> U,
transform: (V) -> U,
): U {
contract {
callsInPlace(default, InvocationKind.AT_MOST_ONCE)
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> transform(value)
is Err -> default(error)
return when {
isOk -> transform(value)
else -> default(error)
}
}
@ -273,35 +281,40 @@ public inline fun <V, E, U> Result<V, E>.mapOrElse(default: (E) -> U, transform:
public inline infix fun <V, E, U> Result<Iterable<V>, E>.mapAll(transform: (V) -> Result<U, E>): Result<List<U>, E> {
return map { iterable ->
iterable.map { element ->
when (val transformed = transform(element)) {
is Ok -> transformed.value
is Err -> return transformed
val transformed = transform(element)
when {
transformed.isOk -> transformed.value
else -> return transformed.asErr()
}
}
}
}
/**
* Returns the [transformation][transform] of the [value][Ok.value] if this [Result] is [Ok]
* and satisfies the given [predicate], otherwise this [Result].
* Returns the [transformation][transform] of the [value][Result.value] if this result
* [is ok][Result.isOk] and satisfies the given [predicate], otherwise [this].
*
* @see [takeIf]
*/
public inline fun <V, E> Result<V, E>.toErrorIf(predicate: (V) -> Boolean, transform: (V) -> E): Result<V, E> {
public inline fun <V, E> Result<V, E>.toErrorIf(
predicate: (V) -> Boolean,
transform: (V) -> E,
): Result<V, E> {
contract {
callsInPlace(predicate, InvocationKind.AT_MOST_ONCE)
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when {
this is Ok && predicate(value) -> Err(transform(value))
isOk && predicate(value) -> Err(transform(value))
else -> this
}
}
/**
* Returns the supplied [error] if this [Result] is [Ok] and the [value][Ok.value] is `null`,
* otherwise this [Result].
* Returns the supplied [error] if this result [is ok][Result.isOk] and the [value][Result.value]
* is `null`, otherwise [this].
*
* @see [toErrorIf]
*/
@ -310,43 +323,37 @@ public inline fun <V, E> Result<V?, E>.toErrorIfNull(error: () -> E): Result<V,
callsInPlace(error, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> if (value == null) {
Err(error())
} else {
Ok(value)
}
is Err -> this
return when {
isOk && value == null -> Err(error())
isOk && value != null -> this.asOk()
else -> this.asErr()
}
}
/**
* Returns the [transformation][transform] of the [value][Ok.value] if this [Result] is [Ok]
* and _does not_ satisfy the given [predicate], otherwise this [Result].
* Returns the [transformation][transform] of the [value][Result.value] if this result
* [is ok][Result.isOk] and _does not_ satisfy the given [predicate], otherwise [this].
*
* @see [takeUnless]
*/
public inline fun <V, E> Result<V, E>.toErrorUnless(predicate: (V) -> Boolean, transform: (V) -> E): Result<V, E> {
public inline fun <V, E> Result<V, E>.toErrorUnless(
predicate: (V) -> Boolean,
transform: (V) -> E,
): Result<V, E> {
contract {
callsInPlace(predicate, InvocationKind.AT_MOST_ONCE)
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> if (!predicate(value)) {
Err(transform(value))
} else {
this
}
is Err -> this
return when {
isOk && !predicate(value) -> Err(transform(value))
else -> this
}
}
/**
* Returns the supplied [error] unless this [Result] is [Ok] and the [value][Ok.value] is `null`,
* otherwise this [Result].
* Returns the supplied [error] unless this result [is ok][Result.isOk] and the
* [value][Result.value] is `null`, otherwise [this].
*
* @see [toErrorUnless]
*/
@ -355,13 +362,8 @@ public inline fun <V, E> Result<V, E>.toErrorUnlessNull(error: () -> E): Result<
callsInPlace(error, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> if (value == null) {
this
} else {
Err(error())
}
is Err -> Err(error())
return when {
isOk && value == null -> this
else -> Err(error())
}
}

View File

@ -4,7 +4,7 @@ import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/**
* Invokes an [action] if this [Result] is [Ok].
* Invokes an [action] if this result [is ok][Result.isOk].
*
* - Rust: [Result.inspect](https://doc.rust-lang.org/std/result/enum.Result.html#method.inspect)
*/
@ -13,7 +13,7 @@ public inline infix fun <V, E> Result<V, E>.onSuccess(action: (V) -> Unit): Resu
callsInPlace(action, InvocationKind.AT_MOST_ONCE)
}
if (this is Ok) {
if (isOk) {
action(value)
}
@ -21,7 +21,7 @@ public inline infix fun <V, E> Result<V, E>.onSuccess(action: (V) -> Unit): Resu
}
/**
* Invokes an [action] if this [Result] is [Err].
* Invokes an [action] if this result [is an error][Result.isErr].
*
* - Rust [Result.inspect_err](https://doc.rust-lang.org/std/result/enum.Result.html#method.inspect_err)
*/
@ -30,7 +30,7 @@ public inline infix fun <V, E> Result<V, E>.onFailure(action: (E) -> Unit): Resu
callsInPlace(action, InvocationKind.AT_MOST_ONCE)
}
if (this is Err) {
if (isErr) {
action(error)
}

View File

@ -4,14 +4,14 @@ import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/**
* Returns [result] if this [Result] is [Err], otherwise this [Ok].
* Returns [result] if this result [is an error][Result.isErr], otherwise [this].
*
* - Rust: [Result.or](https://doc.rust-lang.org/std/result/enum.Result.html#method.or)
*/
public infix fun <V, E, F> Result<V, E>.or(result: Result<V, F>): Result<V, F> {
return when (this) {
is Ok -> this
is Err -> result
return when {
isOk -> this.asOk()
else -> result
}
}
@ -25,8 +25,8 @@ public inline infix fun <V, E, F> Result<V, E>.or(result: () -> Result<V, F>): R
}
/**
* Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err],
* otherwise this [Ok].
* Returns the [transformation][transform] of the [error][Result.error] if this result
* [is an error][Result.isErr], otherwise [this].
*
* - Rust: [Result.or_else](https://doc.rust-lang.org/std/result/enum.Result.html#method.or_else)
*/
@ -35,25 +35,26 @@ public inline infix fun <V, E, F> Result<V, E>.orElse(transform: (E) -> Result<V
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> this
is Err -> transform(error)
return when {
isOk -> this.asOk()
else -> transform(error)
}
}
/**
* Throws the [error][Err.error] if this [Result] is [Err], otherwise returns this [Ok].
* Throws the [error][Result.error] if this result [is an error][Result.isErr], otherwise returns
* [this].
*/
public fun <V, E : Throwable> Result<V, E>.orElseThrow(): Ok<V> {
return when (this) {
is Ok -> this
is Err -> throw error
public fun <V, E : Throwable> Result<V, E>.orElseThrow(): Result<V, Nothing> {
return when {
isOk -> this.asOk()
else -> throw error
}
}
/**
* Throws the [error][Err.error] if this [Result] is an [Err] and satisfies the given
* [predicate], otherwise returns this [Result].
* Throws the [error][Result.error] if this result [is an error][Result.isErr] and satisfies the
* given [predicate], otherwise returns [this].
*
* @see [takeIf]
*/
@ -63,14 +64,14 @@ public inline fun <V, E : Throwable> Result<V, E>.throwIf(predicate: (E) -> Bool
}
return when {
this is Err && predicate(error) -> throw error
isErr && predicate(error) -> throw error
else -> this
}
}
/**
* Throws the [error][Err.error] if this [Result] is an [Err] and _does not_ satisfy the
* given [predicate], otherwise returns this [Result].
* Throws the [error][Result.error] if this result [is an error][Result.isErr] and _does not_
* satisfy the given [predicate], otherwise returns [this].
*
* @see [takeUnless]
*/
@ -79,12 +80,8 @@ public inline fun <V, E : Throwable> Result<V, E>.throwUnless(predicate: (E) ->
callsInPlace(predicate, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> this
is Err -> if (!predicate(error)) {
throw error
} else {
this
}
return when {
isErr && !predicate(error) -> throw error
else -> this
}
}

View File

@ -4,54 +4,58 @@ import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/**
* Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err],
* otherwise this [Ok].
* Returns the [transformation][transform] of the [error][Result.error] if this result
* [is an error][Result.isErr], otherwise [this].
*/
public inline infix fun <V, E> Result<V, E>.recover(transform: (E) -> V): Ok<V> {
public inline infix fun <V, E> Result<V, E>.recover(transform: (E) -> V): Result<V, Nothing> {
contract {
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> this
is Err -> Ok(transform(error))
return when {
isOk -> this.asOk()
else -> Ok(transform(error))
}
}
/**
* Returns the [transformation][transform] of the [error][Err.error], catching and encapsulating any
* thrown exception as a failure if this [Result] is [Err], otherwise this [Ok].
* Returns the [transformation][transform] of the [error][Result.error] if this result
* [is an error][Result.isErr], catching and encapsulating any thrown exception as an [Err],
* otherwise [this].
*/
public inline infix fun <V, E> Result<V, E>.recoverCatching(transform: (E) -> V): Result<V, Throwable> {
contract {
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> this
is Err -> runCatching { transform(error) }
return when {
isOk -> this.asOk()
else -> runCatching { transform(error) }
}
}
/**
* Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err]
* and satisfies the given [predicate], otherwise this [Result].
* Returns the [transformation][transform] of the [error][Result.error] if this result
* [is an error][Result.isErr] and satisfies the given [predicate], otherwise [this].
*/
public inline fun <V, E> Result<V, E>.recoverIf(predicate: (E) -> Boolean, transform: (E) -> V): Result<V, E> {
public inline fun <V, E> Result<V, E>.recoverIf(
predicate: (E) -> Boolean,
transform: (E) -> V,
): Result<V, E> {
contract {
callsInPlace(predicate, InvocationKind.AT_MOST_ONCE)
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when {
this is Err && predicate(error) -> Ok(transform(error))
isErr && predicate(error) -> Ok(transform(error))
else -> this
}
}
/**
* Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err]
* and _does not_ satisfy the given [predicate], otherwise this [Result].
* Returns the [transformation][transform] of the [error][Result.error] if this result
* [is an error][Result.isErr] and _does not_ satisfy the given [predicate], otherwise [this].
*/
public inline fun <V, E> Result<V, E>.recoverUnless(predicate: (E) -> Boolean, transform: (E) -> V): Result<V, E> {
contract {
@ -60,29 +64,29 @@ public inline fun <V, E> Result<V, E>.recoverUnless(predicate: (E) -> Boolean, t
}
return when {
this is Err && !predicate(error) -> Ok(transform(error))
isErr && !predicate(error) -> Ok(transform(error))
else -> this
}
}
/**
* Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err],
* otherwise this [Result].
* Returns the [transformation][transform] of the [error][Result.error] if this result
* [is an error][Result.isErr], otherwise [this].
*/
public inline fun <V, E> Result<V, E>.andThenRecover(transform: (E) -> Result<V, E>): Result<V, E> {
contract {
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> this
is Err -> transform(error)
return when {
isOk -> this
else -> transform(error)
}
}
/**
* Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err] and
* satisfies the given [predicate], otherwise this [Result].
* Returns the [transformation][transform] of the [error][Result.error] if this result
* [is an error][Result.isErr] and satisfies the given [predicate], otherwise [this].
*/
public inline fun <V, E> Result<V, E>.andThenRecoverIf(
predicate: (E) -> Boolean,
@ -94,14 +98,14 @@ public inline fun <V, E> Result<V, E>.andThenRecoverIf(
}
return when {
this is Err && predicate(error) -> transform(error)
isErr && predicate(error) -> transform(error)
else -> this
}
}
/**
* Returns the [transformation][transform] of the [error][Err.error] if this [Result] is [Err]
* and _does not_ satisfy the given [predicate], otherwise this [Result].
* Returns the [transformation][transform] of the [error][Result.error] if this result
* [is an error][Result.isErr] and _does not_ satisfy the given [predicate], otherwise [this].
*/
public inline fun <V, E> Result<V, E>.andThenRecoverUnless(
predicate: (E) -> Boolean,
@ -113,7 +117,7 @@ public inline fun <V, E> Result<V, E>.andThenRecoverUnless(
}
return when {
this is Err && !predicate(error) -> transform(error)
isErr && !predicate(error) -> transform(error)
else -> this
}
}

View File

@ -1,5 +1,39 @@
package com.github.michaelbull.result
/**
* Returns a [Result] that [is ok][Result.isOk] and contains a [value][Result.value].
*/
@Suppress("FunctionName", "DEPRECATION")
public fun <V> Ok(value: V): Result<V, Nothing> {
return Ok(value, null)
}
/**
* Returns a [Result] that [is an error][Result.isErr] and contains an [error][Result.error].
*/
@Suppress("FunctionName", "DEPRECATION")
public fun <E> Err(error: E): Result<Nothing, E> {
return Err(error, null)
}
/**
* Unsafely casts this [Result<V, E>][Result] to [Result<U, Nothing>][Result], to be used inside
* an explicit [isOk][Result.isOk] or [isErr][Result.isErr] guard.
*/
@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
public inline fun <V, E, U> Result<V, E>.asOk(): Result<U, Nothing> {
return this as Result<U, Nothing>
}
/**
* Unsafely casts this [Result<V, E>][Result] to [Result<Nothing, F>][Result], to be used inside
* an explicit [isOk][Result.isOk] or [isErr][Result.isErr] guard.
*/
@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
public inline fun <V, E, F> Result<V, E>.asErr(): Result<Nothing, F> {
return this as Result<Nothing, F>
}
/**
* [Result] is a type that represents either success ([Ok]) or failure ([Err]).
*
@ -9,6 +43,12 @@ package com.github.michaelbull.result
*/
public sealed class Result<out V, out E> {
public abstract val value: V
public abstract val error: E
public abstract val isOk: Boolean
public abstract val isErr: Boolean
public abstract operator fun component1(): V?
public abstract operator fun component2(): E?
@ -32,7 +72,22 @@ public sealed class Result<out V, out E> {
/**
* Represents a successful [Result], containing a [value].
*/
public class Ok<out V>(public val value: V) : Result<V, Nothing>() {
@Deprecated(
message = "Using Ok as a return type is deprecated.",
replaceWith = ReplaceWith("Result<V, Nothing>"),
)
public class Ok<out V> internal constructor(
override val value: V,
@Suppress("UNUSED_PARAMETER") placeholder: Any?,
) : Result<V, Nothing>() {
override val error: Nothing
get() {
throw NoSuchElementException()
}
override val isOk: Boolean = true
override val isErr: Boolean = false
override fun component1(): V = value
override fun component2(): Nothing? = null
@ -55,7 +110,22 @@ public class Ok<out V>(public val value: V) : Result<V, Nothing>() {
/**
* Represents a failed [Result], containing an [error].
*/
public class Err<out E>(public val error: E) : Result<Nothing, E>() {
@Deprecated(
message = "Using Err as a return type is deprecated.",
replaceWith = ReplaceWith("Result<Nothing, E>"),
)
public class Err<out E> internal constructor(
override val error: E,
@Suppress("UNUSED_PARAMETER") placeholder: Any?,
) : Result<Nothing, E>() {
override val value: Nothing
get() {
throw NoSuchElementException()
}
override val isOk: Boolean = false
override val isErr: Boolean = true
override fun component1(): Nothing? = null
override fun component2(): E = error

View File

@ -1,8 +1,10 @@
package com.github.michaelbull.result
/**
* Returns an [Iterator] over the possibly contained [value][Ok.value].
* The iterator yields one [value][Ok.value] if the [Result] is [Ok], otherwise throws [NoSuchElementException].
* Returns an [Iterator] over the possibly contained [value][Result.value].
*
* The iterator yields one [value][Result.value] if this result [is ok][Result.isOk], otherwise
* throws [NoSuchElementException].
*
* - Rust: [Result.iter](https://doc.rust-lang.org/std/result/enum.Result.html#method.iter)
*/
@ -11,8 +13,10 @@ public fun <V, E> Result<V, E>.iterator(): Iterator<V> {
}
/**
* Returns a [MutableIterator] over the possibly contained [value][Ok.value].
* The iterator yields one [value][Ok.value] if the [Result] is [Ok], otherwise throws [NoSuchElementException].
* Returns a [MutableIterator] over the possibly contained [value][Result.value].
*
* The iterator yields one [value][Result.value] if this result [is ok][Result.isOk], otherwise
* throws [NoSuchElementException].
*
* - Rust: [Result.iter_mut](https://doc.rust-lang.org/std/result/enum.Result.html#method.iter_mut)
*/
@ -23,31 +27,29 @@ public fun <V, E> Result<V, E>.mutableIterator(): MutableIterator<V> {
private class ResultIterator<out V, out E>(private val result: Result<V, E>) : MutableIterator<V> {
/**
* A flag indicating whether this [Iterator] has [yielded] its [Result].
* A flag indicating whether this [Iterator] has [yielded] the [value][Result.value] of the
* [result].
*/
private var yielded = false
/**
* @return `true` if the [value][Ok.value] is not [yielded] and [Ok], `false` otherwise.
* @return `true` if this [Iterator] has [yielded] the [value][Result.value] of the [result],
* `false` otherwise.
*/
override fun hasNext(): Boolean {
if (yielded) {
return false
}
return when (result) {
is Ok -> true
is Err -> false
}
return !yielded && result.isOk
}
/**
* Returns the [Result's][Result] [value][Ok.value] if not [yielded] and [Ok].
* @throws NoSuchElementException if the [Result] is [yielded] or is not [Ok].
* Returns the [value][Result.value] of the [result] if not [yielded] and the result
* [is ok][Result.isOk].
*
* @throws NoSuchElementException if already [yielded] or the result
* [is an error][Result.isErr].
*/
override fun next(): V {
if (!yielded && result is Ok) {
yielded = true
if (hasNext()) {
remove()
return result.value
} else {
throw NoSuchElementException()
@ -55,7 +57,7 @@ private class ResultIterator<out V, out E>(private val result: Result<V, E>) : M
}
/**
* Flags this [Iterator] as having [yielded] its [Result].
* Flags this [Iterator] as having [yielded] the [value][Result.value] of the [result].
*/
override fun remove() {
yielded = true

View File

@ -6,20 +6,21 @@ import kotlin.contracts.contract
public class UnwrapException(message: String) : Exception(message)
/**
* Unwraps a [Result], yielding the [value][Ok.value].
* Returns the [value][Result.value] if this result [is ok][Result.isOk], otherwise throws an
* [UnwrapException].
*
* - Rust: [Result.unwrap](https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap)
*
* @throws UnwrapException if the [Result] is an [Err], with a message containing the [error][Err.error].
* @throws UnwrapException if this result [is an error][Result.isErr].
*/
public fun <V, E> Result<V, E>.unwrap(): V {
contract {
returns() implies (this@unwrap is Ok<V>)
}
return when (this) {
is Ok -> value
is Err -> throw UnwrapException("called Result.unwrap on an Err value $error")
return when {
isOk -> value
else -> throw UnwrapException("called Result.unwrap on an Err value $error")
}
}
@ -33,12 +34,15 @@ public infix fun <V, E> Result<V, E>.expect(message: String): V {
}
/**
* Unwraps a [Result], yielding the [value][Ok.value].
* Returns the [value][Result.value] if this result [is ok][Result.isOk], otherwise throws an
* [UnwrapException] with the specified [message].
*
* - Rust: [Result.expect](https://doc.rust-lang.org/std/result/enum.Result.html#method.expect)
*
* @param message The message to include in the [UnwrapException] if the [Result] is an [Err].
* @throws UnwrapException if the [Result] is an [Err], with the specified [message].
* @param message The message to include in the [UnwrapException] if this result
* [is an error][Result.isErr].
*
* @throws UnwrapException if this result [is an error][Result.isErr].
*/
public inline infix fun <V, E> Result<V, E>.expect(message: () -> Any): V {
contract {
@ -46,27 +50,28 @@ public inline infix fun <V, E> Result<V, E>.expect(message: () -> Any): V {
returns() implies (this@expect is Ok<V>)
}
return when (this) {
is Ok -> value
is Err -> throw UnwrapException("${message()} $error")
return when {
isOk -> value
else -> throw UnwrapException("${message()} $error")
}
}
/**
* Unwraps a [Result], yielding the [error][Err.error].
* Returns the [error][Result.error] if this result [is an error][Result.isErr], otherwise throws
* an [UnwrapException].
*
* - Rust: [Result.unwrap_err](https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap_err)
*
* @throws UnwrapException if the [Result] is [Ok], with a message containing the [value][Ok.value].
* @throws UnwrapException if this result [is ok][Result.isOk].
*/
public fun <V, E> Result<V, E>.unwrapError(): E {
contract {
returns() implies (this@unwrapError is Err<E>)
}
return when (this) {
is Ok -> throw UnwrapException("called Result.unwrapError on an Ok value $value")
is Err -> error
return when {
isErr -> error
else -> throw UnwrapException("called Result.unwrapError on an Ok value $value")
}
}
@ -80,12 +85,15 @@ public infix fun <V, E> Result<V, E>.expectError(message: String): E {
}
/**
* Unwraps a [Result], yielding the [error][Err.error].
* Returns the [error][Result.error] if this result [is an error][Result.isErr], otherwise throws
* an [UnwrapException] with the specified [message].
*
* - Rust: [Result.expect_err](https://doc.rust-lang.org/std/result/enum.Result.html#method.expect_err)
*
* @param message The message to include in the [UnwrapException] if the [Result] is [Ok].
* @throws UnwrapException if the [Result] is [Ok], with the specified [message].
* @param message The message to include in the [UnwrapException] if this result
* [is ok][Result.isOk].
*
* @throws UnwrapException if this result [is ok][Result.isOk].
*/
public inline infix fun <V, E> Result<V, E>.expectError(message: () -> Any): E {
contract {
@ -93,8 +101,8 @@ public inline infix fun <V, E> Result<V, E>.expectError(message: () -> Any): E {
returns() implies (this@expectError is Err<E>)
}
return when (this) {
is Ok -> throw UnwrapException("${message()} $value")
is Err -> error
return when {
isErr -> error
else -> throw UnwrapException("${message()} $value")
}
}

View File

@ -6,8 +6,8 @@ import kotlin.contracts.contract
private typealias Producer<T, E> = () -> Result<T, E>
/**
* Apply a [transformation][transform] to two [Results][Result], if both [Results][Result] are [Ok].
* If not, the first argument which is an [Err] will propagate through.
* Applies the given [transform] function to two [Results][Result], returning early with the first
* [Err] if a transformation fails.
*
* - Elm: http://package.elm-lang.org/packages/elm-lang/core/latest/Result#map2
*/
@ -30,8 +30,8 @@ public inline fun <T1, T2, E, V> zip(
}
/**
* Apply a [transformation][transform] to three [Results][Result], if all [Results][Result] are [Ok].
* If not, the first argument which is an [Err] will propagate through.
* Applies the given [transform] function to three [Results][Result], returning early with the
* first [Err] if a transformation fails.
*
* - Elm: http://package.elm-lang.org/packages/elm-lang/core/latest/Result#map3
*/
@ -58,8 +58,8 @@ public inline fun <T1, T2, T3, E, V> zip(
}
/**
* Apply a [transformation][transform] to four [Results][Result], if all [Results][Result] are [Ok].
* If not, the first argument which is an [Err] will propagate through.
* Applies the given [transform] function to four [Results][Result], returning early with the
* first [Err] if a transformation fails.
*
* - Elm: http://package.elm-lang.org/packages/elm-lang/core/latest/Result#map4
*/
@ -90,8 +90,8 @@ public inline fun <T1, T2, T3, T4, E, V> zip(
}
/**
* Apply a [transformation][transform] to five [Results][Result], if all [Results][Result] are [Ok].
* If not, the first argument which is an [Err] will propagate through.
* Applies the given [transform] function to five [Results][Result], returning early with the
* first [Err] if a transformation fails.
*
* - Elm: http://package.elm-lang.org/packages/elm-lang/core/latest/Result#map5
*/
@ -126,8 +126,8 @@ public inline fun <T1, T2, T3, T4, T5, E, V> zip(
}
/**
* Apply a [transformation][transform] to two [Results][Result], if both [Results][Result] are [Ok].
* If not, the all arguments which are [Err] will be collected.
* Applies the given [transform] function to two [Results][Result], collecting any result that
* [is an error][Result.isErr] to a [List].
*/
public inline fun <T1, T2, E, V> zipOrAccumulate(
producer1: () -> Result<T1, E>,
@ -143,10 +143,12 @@ public inline fun <T1, T2, E, V> zipOrAccumulate(
val result1 = producer1()
val result2 = producer2()
return if (
result1 is Ok &&
result2 is Ok
) {
val results = listOf(
producer1(),
producer2(),
)
return if (results.allOk()) {
val transformed = transform(
result1.value,
result2.value,
@ -154,18 +156,13 @@ public inline fun <T1, T2, E, V> zipOrAccumulate(
Ok(transformed)
} else {
val errors = listOf(
result1,
result2
).mapNotNull { it.getError() }
Err(errors)
Err(results.filterErrors())
}
}
/**
* Apply a [transformation][transform] to three [Results][Result], if all [Results][Result] are [Ok].
* If not, the all arguments which are [Err] will be collected.
* Applies the given [transform] function to three [Results][Result], collecting any result that
* [is an error][Result.isErr] to a [List].
*/
public inline fun <T1, T2, T3, E, V> zipOrAccumulate(
producer1: () -> Result<T1, E>,
@ -184,11 +181,13 @@ public inline fun <T1, T2, T3, E, V> zipOrAccumulate(
val result2 = producer2()
val result3 = producer3()
return if (
result1 is Ok &&
result2 is Ok &&
result3 is Ok
) {
val results = listOf(
result1,
result2,
result3,
)
return if (results.allOk()) {
val transformed = transform(
result1.value,
result2.value,
@ -197,19 +196,13 @@ public inline fun <T1, T2, T3, E, V> zipOrAccumulate(
Ok(transformed)
} else {
val errors = listOf(
result1,
result2,
result3
).mapNotNull { it.getError() }
Err(errors)
Err(results.filterErrors())
}
}
/**
* Apply a [transformation][transform] to four [Results][Result], if all [Results][Result] are [Ok].
* If not, the all arguments which are [Err] will be collected.
* Applies the given [transform] function to four [Results][Result], collecting any result that
* [is an error][Result.isErr] to a [List].
*/
public inline fun <T1, T2, T3, T4, E, V> zipOrAccumulate(
producer1: () -> Result<T1, E>,
@ -231,35 +224,30 @@ public inline fun <T1, T2, T3, T4, E, V> zipOrAccumulate(
val result3 = producer3()
val result4 = producer4()
return if (
result1 is Ok &&
result2 is Ok &&
result3 is Ok &&
result4 is Ok
) {
val results = listOf(
result1,
result2,
result3,
result4,
)
return if (results.allOk()) {
val transformed = transform(
result1.value,
result2.value,
result3.value,
result4.value
result4.value,
)
Ok(transformed)
} else {
val errors = listOf(
result1,
result2,
result3,
result4
).mapNotNull { it.getError() }
Err(errors)
Err(results.filterErrors())
}
}
/**
* Apply a [transformation][transform] to five [Results][Result], if all [Results][Result] are [Ok].
* If not, the all arguments which are [Err] will be collected.
* Applies the given [transform] function to five [Results][Result], collecting any result that
* [is an error][Result.isErr] to a [List].
*/
public inline fun <T1, T2, T3, T4, T5, E, V> zipOrAccumulate(
producer1: () -> Result<T1, E>,
@ -284,31 +272,25 @@ public inline fun <T1, T2, T3, T4, T5, E, V> zipOrAccumulate(
val result4 = producer4()
val result5 = producer5()
return if (
result1 is Ok &&
result2 is Ok &&
result3 is Ok &&
result4 is Ok &&
result5 is Ok
) {
val results = listOf(
result1,
result2,
result3,
result4,
result5
)
return if (results.allOk()) {
val transformed = transform(
result1.value,
result2.value,
result3.value,
result4.value,
result5.value
result5.value,
)
Ok(transformed)
} else {
val errors = listOf(
result1,
result2,
result3,
result4,
result5
).mapNotNull { it.getError() }
Err(errors)
Err(results.filterErrors())
}
}