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 package com.github.michaelbull.result.coroutines
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result 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.CancellationException
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -12,13 +13,31 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
import kotlin.coroutines.CoroutineContext
/** /**
* Suspending variant of [binding][com.github.michaelbull.result.binding]. * Calls the specified function [block] with [CoroutineBindingScope] as its receiver and returns
* The suspendable [block] runs in a new [CoroutineScope], inheriting the parent [CoroutineContext]. * its [Result].
* This new scope is [cancelled][CoroutineScope.cancel] once a failing bind is encountered, eagerly cancelling all *
* child [jobs][Job]. * 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> { public suspend inline fun <V, E> coroutineBinding(crossinline block: suspend CoroutineBindingScope<E>.() -> V): Result<V, E> {
contract { contract {
@ -55,11 +74,12 @@ internal class CoroutineBindingScopeImpl<E>(
var result: Result<Nothing, E>? = null var result: Result<Nothing, E>? = null
override suspend fun <V> Result<V, E>.bind(): V { override suspend fun <V> Result<V, E>.bind(): V {
return when (this) { return if (isOk) {
is Ok -> value value
is Err -> mutex.withLock { } else {
mutex.withLock {
if (result == null) { if (result == null) {
result = this result = this.asErr()
coroutineContext.cancel(BindCancellationException) coroutineContext.cancel(BindCancellationException)
} }

View File

@ -4,14 +4,14 @@ import kotlin.contracts.InvocationKind
import kotlin.contracts.contract 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) * - 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> { public infix fun <V, E, U> Result<V, E>.and(result: Result<U, E>): Result<U, E> {
return when (this) { return when {
is Ok -> result isOk -> result
is Err -> this 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] * 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) * - 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) * - 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) callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
} }
return when (this) { return when {
is Ok -> transform(value) isOk -> transform(value)
is Err -> this else -> this.asErr()
} }
} }

View File

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

View File

@ -4,7 +4,7 @@ import kotlin.contracts.InvocationKind
import kotlin.contracts.contract 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) * - 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) * - 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>) returns(null) implies (this@get is Err<E>)
} }
return when (this) { return when {
is Ok -> value isOk -> value
is Err -> null 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) * - 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>) returnsNotNull() implies (this@getError is Err<E>)
} }
return when (this) { return when {
is Ok -> null isErr -> error
is Err -> 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) * - 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) * - 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) * - 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]. * @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 { public infix fun <V, E> Result<V, E>.getOr(default: V): V {
return when (this) { return when {
is Ok -> value isOk -> value
is Err -> default 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) * - 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]. * @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 { public infix fun <V, E> Result<V, E>.getErrorOr(default: E): E {
return when (this) { return when {
is Ok -> default isOk -> default
is Err -> error 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 * Returns the [value][Result.value] if this result [is ok][Result.isOk], otherwise the
* [transformation][transform] of the [error][Err.error]. * [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) * - 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) * - 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) callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
} }
return when (this) { return when {
is Ok -> value isOk -> value
is Err -> transform(error) else -> transform(error)
} }
} }
/** /**
* Returns the [error][Err.error] if this [Result] is [Err], otherwise the * Returns the [error][Result.error] if this result [is an error][Result.isErr], otherwise the
* [transformation][transform] of the [value][Ok.value]. * [transformation][transform] of the [value][Result.value].
*/ */
public inline infix fun <V, E> Result<V, E>.getErrorOrElse(transform: (V) -> E): E { public inline infix fun <V, E> Result<V, E>.getErrorOrElse(transform: (V) -> E): E {
contract { contract {
callsInPlace(transform, InvocationKind.AT_MOST_ONCE) callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
} }
return when (this) { return when {
is Ok -> transform(value) isErr -> error
is Err -> error else -> transform(value)
} }
} }
/** /**
* Returns the [value][Ok.value] if this [Result] is [Ok], otherwise throws the * Returns the [value][Result.value] if this result [is ok][Result.isOk], otherwise throws the
* [error][Err.error]. * [error][Result.error].
* *
* This is functionally equivalent to [`getOrElse { throw it }`][getOrElse]. * 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>) returns() implies (this@getOrThrow is Ok<V>)
} }
return when (this) { return when {
is Ok -> value isOk -> value
is Err -> throw error else -> throw error
} }
} }
/** /**
* Returns the [value][Ok.value] if this [Result] is [Ok], otherwise throws the * Returns the [value][Result.value] if this result [is ok][Result.isOk], otherwise throws the
* [transformation][transform] of the [error][Err.error] to a [Throwable]. * [transformation][transform] of the [error][Result.error] to a [Throwable].
*/ */
public inline infix fun <V, E> Result<V, E>.getOrThrow(transform: (E) -> Throwable): V { public inline infix fun <V, E> Result<V, E>.getOrThrow(transform: (E) -> Throwable): V {
contract { contract {
@ -148,21 +149,21 @@ public inline infix fun <V, E> Result<V, E>.getOrThrow(transform: (E) -> Throwab
callsInPlace(transform, InvocationKind.AT_MOST_ONCE) callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
} }
return when (this) { return when {
is Ok -> value isOk -> value
is Err -> throw transform(error) else -> throw transform(error)
} }
} }
/** /**
* Merges this [Result<V, E>][Result] to [U], returning the [value][Ok.value] if this [Result] is [Ok], otherwise the * Merges this [Result<V, E>][Result] to [U], returning the [value][Result.value] if this result
* [error][Err.error]. * [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) * - 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 { public fun <V : U, E : U, U> Result<V, E>.merge(): U {
return when (this) { return when {
is Ok -> value isOk -> value
is Err -> error else -> error
} }
} }

View File

@ -1,25 +1,26 @@
package com.github.michaelbull.result 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> { public fun <V, E> Iterable<Result<V, E>>.filterValues(): List<V> {
return filterValuesTo(ArrayList()) 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> { public fun <V, E> Iterable<Result<V, E>>.filterErrors(): List<E> {
return filterErrorsTo(ArrayList()) 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 { public fun <V, E, C : MutableCollection<in V>> Iterable<Result<V, E>>.filterValuesTo(destination: C): C {
for (element in this) { for (element in this) {
if (element is Ok<V>) { if (element.isOk) {
destination.add(element.value) 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 { public fun <V, E, C : MutableCollection<in E>> Iterable<Result<V, E>>.filterErrorsTo(destination: C): C {
for (element in this) { for (element in this) {
if (element is Err<E>) { if (element.isErr) {
destination.add(element.error) 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 { 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 { 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 { 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 { 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 { 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 { 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 var accumulator = initial
for (element in this) { for (element in this) {
accumulator = when (val result = operation(accumulator, element)) { val result = operation(accumulator, element)
is Ok -> result.value
is Err -> return Err(result.error) 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 * Accumulates value starting with [initial] value and applying [operation] from right to left to
* each element and current accumulator value. * 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, initial: R,
operation: (T, acc: R) -> Result<R, E>, operation: (T, acc: R) -> Result<R, E>,
): 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) val iterator = listIterator(size)
while (iterator.hasPrevious()) { while (iterator.hasPrevious()) {
accumulator = when (val result = operation(iterator.previous(), accumulator)) { val result = operation(iterator.previous(), accumulator)
is Ok -> result.value
is Err -> return Err(result.error) 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 * Combines the specified [results] into a single [Result] (holding a [List]). Elements in the
* are in the same order is the input vararg. * 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) * - 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 * Combines [this] iterable into a single [Result] (holding a [List]). Elements in the returned
* list are in the input [Iterable] order. * 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) * - 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> { public fun <V, E> Iterable<Result<V, E>>.combine(): Result<List<V>, E> {
val values = map { val values = map { result ->
when (it) { when {
is Ok -> it.value result.isOk -> result.value
is Err -> return it 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 * Returns a [List] containing the [value][Result.value] of each element in the specified [results]
* extracted in order. * 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) * - 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 * Returns a [List] containing the [error][Result.error] of each element in the specified [results]
* extracted in order. * 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) * - 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 * Partitions the specified [results] into a [Pair] of [Lists][List]. An element that
* are extracted, in order, to the [first][Pair.first] value. Similarly the [Err] elements are * [is ok][Result.isOk] will appear in the [first][Pair.first] list, whereas an element that
* extracted to the [Pair.second] value. * [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) * - 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 * Partitions this into a [Pair] of [Lists][List]. An element that [is ok][Result.isOk] will appear
* are extracted to the [Pair.second] value. * 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) * - 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 values = mutableListOf<V>()
val errors = mutableListOf<E>() val errors = mutableListOf<E>()
forEach { result -> for (result in this) {
when (result) { if (result.isOk) {
is Ok -> values.add(result.value) values += result.value
is Err -> errors.add(result.error) } 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] * 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 * 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( public inline fun <V, E, U> Iterable<V>.mapResult(
transform: (V) -> Result<U, E>, transform: (V) -> Result<U, E>,
): Result<List<U>, E> { ): Result<List<U>, E> {
val values = map { element -> val values = map { element ->
when (val transformed = transform(element)) { val transformed = transform(element)
is Ok -> transformed.value
is Err -> return transformed 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 * 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 * 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( public inline fun <V, E, U, C : MutableCollection<in U>> Iterable<V>.mapResultTo(
destination: C, destination: C,
transform: (V) -> Result<U, E>, transform: (V) -> Result<U, E>,
): Result<C, E> { ): Result<C, E> {
val values = mapTo(destination) { element -> val values = mapTo(destination) { element ->
when (val transformed = transform(element)) { val transformed = transform(element)
is Ok -> transformed.value
is Err -> return transformed 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 * 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 * 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] * first [Err] if a transformation fails. Elements in the returned list are in the same order as
* order. * [this].
*/ */
public inline fun <V, E, U : Any> Iterable<V>.mapResultNotNull( public inline fun <V, E, U : Any> Iterable<V>.mapResultNotNull(
transform: (V) -> Result<U, E>?, transform: (V) -> Result<U, E>?,
): Result<List<U>, E> { ): Result<List<U>, E> {
val values = mapNotNull { element -> val values = mapNotNull { element ->
when (val transformed = transform(element)) { val transformed = transform(element)
is Ok -> transformed.value
is Err -> return transformed when {
null -> null 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>?, transform: (V) -> Result<U, E>?,
): Result<C, E> { ): Result<C, E> {
val values = mapNotNullTo(destination) { element -> val values = mapNotNullTo(destination) { element ->
when (val transformed = transform(element)) { val transformed = transform(element)
is Ok -> transformed.value
is Err -> return transformed when {
null -> null 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] * 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 * 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] * first [Err] if a transformation fails. Elements in the returned list are in same order as
* order. * [this].
*/ */
public inline fun <V, E, U> Iterable<V>.mapResultIndexed( public inline fun <V, E, U> Iterable<V>.mapResultIndexed(
transform: (index: Int, V) -> Result<U, E>, transform: (index: Int, V) -> Result<U, E>,
): Result<List<U>, E> { ): Result<List<U>, E> {
val values = mapIndexed { index, element -> val values = mapIndexed { index, element ->
when (val transformed = transform(index, element)) { val transformed = transform(index, element)
is Ok -> transformed.value
is Err -> return transformed 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>, transform: (index: Int, V) -> Result<U, E>,
): Result<C, E> { ): Result<C, E> {
val values = mapIndexedTo(destination) { index, element -> val values = mapIndexedTo(destination) { index, element ->
when (val transformed = transform(index, element)) { val transformed = transform(index, element)
is Ok -> transformed.value
is Err -> return transformed 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 * 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 * 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 * 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( public inline fun <V, E, U : Any> Iterable<V>.mapResultIndexedNotNull(
transform: (index: Int, V) -> Result<U, E>?, transform: (index: Int, V) -> Result<U, E>?,
): Result<List<U>, E> { ): Result<List<U>, E> {
val values = mapIndexedNotNull { index, element -> val values = mapIndexedNotNull { index, element ->
when (val transformed = transform(index, element)) { val transformed = transform(index, element)
is Ok -> transformed.value
is Err -> return transformed when {
null -> null 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>?, transform: (index: Int, V) -> Result<U, E>?,
): Result<C, E> { ): Result<C, E> {
val values = mapIndexedNotNullTo(destination) { index, element -> val values = mapIndexedNotNullTo(destination) { index, element ->
when (val transformed = transform(index, element)) { val transformed = transform(index, element)
is Ok -> transformed.value
is Err -> return transformed when {
null -> null 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] * 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) * - 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) * - 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) callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
} }
return when (this) { return when {
is Ok -> Ok(transform(value)) isOk -> Ok(transform(value))
is Err -> this else -> this.asErr()
} }
} }
/** /**
* Maps this [Result<V, Throwable>][Result] to [Result<U, Throwable>][Result] by either applying * 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 * the [transform] function to the [value][Result.value] if this result [is ok][Result.isOk], or
* [Err]. * returning [this].
* *
* This function catches any [Throwable] exception thrown by [transform] function and encapsulates * 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) * - 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) * - 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) callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
} }
return when (this) { return when {
is Ok -> runCatching { transform(value) } isOk -> runCatching { transform(value) }
is Err -> this 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] * - 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>? { public inline fun <V, E> Result<V?, E>.transpose(): Result<V, E>? {
return when (this) { return when {
is Ok -> if (value == null) { isOk && value == null -> null
null isOk && value != null -> this.asOk()
} else { else -> this.asErr()
Ok(value)
}
is Err -> this
} }
} }
@ -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) * - 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> { public fun <V, E> Result<Result<V, E>, E>.flatten(): Result<V, E> {
return when (this) { return when {
is Ok -> value isOk -> value
is Err -> this else -> this.asErr()
} }
} }
/** /**
* Maps this [Result<V, E>][Result] to [Result<U, E>][Result] by either applying the [transform] * 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]. * 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 * 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]. * 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) callsInPlace(failure, InvocationKind.AT_MOST_ONCE)
} }
return when (this) { return when {
is Ok -> success(value) isOk -> success(value)
is Err -> failure(error) else -> failure(error)
} }
} }
/** /**
* Maps this [Result<V, E>][Result] to [U] by applying either the [success] function if this * 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]. * 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] * 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]. * 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) callsInPlace(failure, InvocationKind.AT_MOST_ONCE)
} }
return when (this) { return when {
is Ok -> success(value) isOk -> success(value)
is Err -> failure(error) else -> failure(error)
} }
} }
/** /**
* Maps this [Result<V, E>][Result] to [Result<U, F>][Result] by applying either the [success] * 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. * 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) callsInPlace(failure, InvocationKind.AT_MOST_ONCE)
} }
return when (this) { return when {
is Ok -> Ok(success(value)) isOk -> Ok(success(value))
is Err -> Err(failure(error)) else -> Err(failure(error))
} }
} }
/** /**
* Maps this [Result<V, E>][Result] to [Result<U, F>][Result] by applying either the [success] * 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. * 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) callsInPlace(failure, InvocationKind.AT_MOST_ONCE)
} }
return when (this) { return when {
is Ok -> success(value) isOk -> success(value)
is Err -> failure(error) else -> failure(error)
} }
} }
/** /**
* Maps this [Result<V, E>][Result] to [Result<V, F>][Result] by either applying the [transform] * 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) * - 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) * - 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) callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
} }
return when (this) { return when {
is Ok -> this isErr -> Err(transform(error))
is Err -> Err(transform(error)) else -> this.asOk()
} }
} }
/** /**
* Maps this [Result<V, E>][Result] to [U] by either applying the [transform] function to the * 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 * [value][Result.value] if this result [is ok][Result.isOk], or returning the [default] if this
* [Err]. * result [is an error][Result.isErr].
* *
* - Rust: [Result.map_or](https://doc.rust-lang.org/std/result/enum.Result.html#method.map_or) * - 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 { contract {
callsInPlace(transform, InvocationKind.AT_MOST_ONCE) callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
} }
return when (this) { return when {
is Ok -> transform(value) isOk -> transform(value)
is Err -> default else -> default
} }
} }
/** /**
* Maps this [Result<V, E>][Result] to [U] by applying either the [transform] function if this * 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 * result [is ok][Result.isOk], or the [default] function if this result
* functions must return the same type ([U]). * [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) * - 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 { contract {
callsInPlace(default, InvocationKind.AT_MOST_ONCE) callsInPlace(default, InvocationKind.AT_MOST_ONCE)
callsInPlace(transform, InvocationKind.AT_MOST_ONCE) callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
} }
return when (this) { return when {
is Ok -> transform(value) isOk -> transform(value)
is Err -> default(error) 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> { public inline infix fun <V, E, U> Result<Iterable<V>, E>.mapAll(transform: (V) -> Result<U, E>): Result<List<U>, E> {
return map { iterable -> return map { iterable ->
iterable.map { element -> iterable.map { element ->
when (val transformed = transform(element)) { val transformed = transform(element)
is Ok -> transformed.value
is Err -> return transformed when {
transformed.isOk -> transformed.value
else -> return transformed.asErr()
} }
} }
} }
} }
/** /**
* Returns the [transformation][transform] of the [value][Ok.value] if this [Result] is [Ok] * Returns the [transformation][transform] of the [value][Result.value] if this result
* and satisfies the given [predicate], otherwise this [Result]. * [is ok][Result.isOk] and satisfies the given [predicate], otherwise [this].
* *
* @see [takeIf] * @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 { contract {
callsInPlace(predicate, InvocationKind.AT_MOST_ONCE) callsInPlace(predicate, InvocationKind.AT_MOST_ONCE)
callsInPlace(transform, InvocationKind.AT_MOST_ONCE) callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
} }
return when { return when {
this is Ok && predicate(value) -> Err(transform(value)) isOk && predicate(value) -> Err(transform(value))
else -> this else -> this
} }
} }
/** /**
* Returns the supplied [error] if this [Result] is [Ok] and the [value][Ok.value] is `null`, * Returns the supplied [error] if this result [is ok][Result.isOk] and the [value][Result.value]
* otherwise this [Result]. * is `null`, otherwise [this].
* *
* @see [toErrorIf] * @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) callsInPlace(error, InvocationKind.AT_MOST_ONCE)
} }
return when (this) { return when {
is Ok -> if (value == null) { isOk && value == null -> Err(error())
Err(error()) isOk && value != null -> this.asOk()
} else { else -> this.asErr()
Ok(value)
}
is Err -> this
} }
} }
/** /**
* Returns the [transformation][transform] of the [value][Ok.value] if this [Result] is [Ok] * Returns the [transformation][transform] of the [value][Result.value] if this result
* and _does not_ satisfy the given [predicate], otherwise this [Result]. * [is ok][Result.isOk] and _does not_ satisfy the given [predicate], otherwise [this].
* *
* @see [takeUnless] * @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 { contract {
callsInPlace(predicate, InvocationKind.AT_MOST_ONCE) callsInPlace(predicate, InvocationKind.AT_MOST_ONCE)
callsInPlace(transform, InvocationKind.AT_MOST_ONCE) callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
} }
return when (this) { return when {
is Ok -> if (!predicate(value)) { isOk && !predicate(value) -> Err(transform(value))
Err(transform(value)) else -> this
} else {
this
}
is Err -> this
} }
} }
/** /**
* Returns the supplied [error] unless this [Result] is [Ok] and the [value][Ok.value] is `null`, * Returns the supplied [error] unless this result [is ok][Result.isOk] and the
* otherwise this [Result]. * [value][Result.value] is `null`, otherwise [this].
* *
* @see [toErrorUnless] * @see [toErrorUnless]
*/ */
@ -355,13 +362,8 @@ public inline fun <V, E> Result<V, E>.toErrorUnlessNull(error: () -> E): Result<
callsInPlace(error, InvocationKind.AT_MOST_ONCE) callsInPlace(error, InvocationKind.AT_MOST_ONCE)
} }
return when (this) { return when {
is Ok -> if (value == null) { isOk && value == null -> this
this else -> Err(error())
} else {
Err(error())
}
is Err -> Err(error())
} }
} }

View File

@ -4,7 +4,7 @@ import kotlin.contracts.InvocationKind
import kotlin.contracts.contract 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) * - 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) callsInPlace(action, InvocationKind.AT_MOST_ONCE)
} }
if (this is Ok) { if (isOk) {
action(value) 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) * - 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) callsInPlace(action, InvocationKind.AT_MOST_ONCE)
} }
if (this is Err) { if (isErr) {
action(error) action(error)
} }

View File

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

View File

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

View File

@ -1,5 +1,39 @@
package com.github.michaelbull.result 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]). * [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 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 component1(): V?
public abstract operator fun component2(): E? 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]. * 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 component1(): V = value
override fun component2(): Nothing? = null 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]. * 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 component1(): Nothing? = null
override fun component2(): E = error override fun component2(): E = error

View File

@ -1,8 +1,10 @@
package com.github.michaelbull.result package com.github.michaelbull.result
/** /**
* Returns an [Iterator] over the possibly contained [value][Ok.value]. * Returns an [Iterator] over the possibly contained [value][Result.value].
* The iterator yields one [value][Ok.value] if the [Result] is [Ok], otherwise throws [NoSuchElementException]. *
* 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) * - 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]. * Returns a [MutableIterator] over the possibly contained [value][Result.value].
* The iterator yields one [value][Ok.value] if the [Result] is [Ok], otherwise throws [NoSuchElementException]. *
* 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) * - 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> { 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 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 { override fun hasNext(): Boolean {
if (yielded) { return !yielded && result.isOk
return false
}
return when (result) {
is Ok -> true
is Err -> false
}
} }
/** /**
* Returns the [Result's][Result] [value][Ok.value] if not [yielded] and [Ok]. * Returns the [value][Result.value] of the [result] if not [yielded] and the result
* @throws NoSuchElementException if the [Result] is [yielded] or is not [Ok]. * [is ok][Result.isOk].
*
* @throws NoSuchElementException if already [yielded] or the result
* [is an error][Result.isErr].
*/ */
override fun next(): V { override fun next(): V {
if (!yielded && result is Ok) { if (hasNext()) {
yielded = true remove()
return result.value return result.value
} else { } else {
throw NoSuchElementException() 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() { override fun remove() {
yielded = true yielded = true

View File

@ -6,20 +6,21 @@ import kotlin.contracts.contract
public class UnwrapException(message: String) : Exception(message) 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) * - 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 { public fun <V, E> Result<V, E>.unwrap(): V {
contract { contract {
returns() implies (this@unwrap is Ok<V>) returns() implies (this@unwrap is Ok<V>)
} }
return when (this) { return when {
is Ok -> value isOk -> value
is Err -> throw UnwrapException("called Result.unwrap on an Err value $error") 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) * - 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]. * @param message The message to include in the [UnwrapException] if this result
* @throws UnwrapException if the [Result] is an [Err], with the specified [message]. * [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 { public inline infix fun <V, E> Result<V, E>.expect(message: () -> Any): V {
contract { 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>) returns() implies (this@expect is Ok<V>)
} }
return when (this) { return when {
is Ok -> value isOk -> value
is Err -> throw UnwrapException("${message()} $error") 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) * - 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 { public fun <V, E> Result<V, E>.unwrapError(): E {
contract { contract {
returns() implies (this@unwrapError is Err<E>) returns() implies (this@unwrapError is Err<E>)
} }
return when (this) { return when {
is Ok -> throw UnwrapException("called Result.unwrapError on an Ok value $value") isErr -> error
is Err -> 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) * - 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]. * @param message The message to include in the [UnwrapException] if this result
* @throws UnwrapException if the [Result] is [Ok], with the specified [message]. * [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 { public inline infix fun <V, E> Result<V, E>.expectError(message: () -> Any): E {
contract { 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>) returns() implies (this@expectError is Err<E>)
} }
return when (this) { return when {
is Ok -> throw UnwrapException("${message()} $value") isErr -> error
is Err -> error else -> throw UnwrapException("${message()} $value")
} }
} }

View File

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