diff --git a/result4k/src/main/kotlin/dev/forkhandles/result4k/iterables.kt b/result4k/src/main/kotlin/dev/forkhandles/result4k/iterables.kt index 6caba05..6a87f29 100644 --- a/result4k/src/main/kotlin/dev/forkhandles/result4k/iterables.kt +++ b/result4k/src/main/kotlin/dev/forkhandles/result4k/iterables.kt @@ -1,19 +1,20 @@ package dev.forkhandles.result4k +@Suppress("UNCHECKED_CAST") fun Iterable>.allValues(): Result, E> = - Success(map { r -> r.onFailure { return it } }) + Success(map { r -> r.onFailure { return it as Result, E> } }) fun Iterable>.anyValues(): List = - filterIsInstance>().map { it.value } + filter { it.isSuccess() }.map { it.unsafeValue } fun Iterable>.partition(): Pair, List> { val oks = mutableListOf() val errs = mutableListOf() forEach { - when (it) { - is Success -> oks.add(it.value) - is Failure -> errs.add(it.reason) + when { + it.isSuccess() -> oks.add(it.unsafeValue) + else -> errs.add(it.unsafeReason) } } return Pair(oks, errs) diff --git a/result4k/src/main/kotlin/dev/forkhandles/result4k/nullables.kt b/result4k/src/main/kotlin/dev/forkhandles/result4k/nullables.kt index c9cb3bf..ceeaf4f 100644 --- a/result4k/src/main/kotlin/dev/forkhandles/result4k/nullables.kt +++ b/result4k/src/main/kotlin/dev/forkhandles/result4k/nullables.kt @@ -21,15 +21,15 @@ inline fun Result.filterNotNull(failureDescription: () -> E) /** * Returns the success value, or null if the Result is a failure. */ -fun Result.valueOrNull() = when (this) { - is Success -> value - is Failure -> null +fun Result.valueOrNull() = when { + isSuccess() -> unsafeValue + else -> null } /** * Returns the failure reason, or null if the Result is a success. */ -fun Result.failureOrNull() = when (this) { - is Success -> null - is Failure -> reason +fun Result.failureOrNull() = when { + isFailure() -> unsafeReason + else -> null } diff --git a/result4k/src/main/kotlin/dev/forkhandles/result4k/result.kt b/result4k/src/main/kotlin/dev/forkhandles/result4k/result.kt index bbe3e48..08ec0d2 100644 --- a/result4k/src/main/kotlin/dev/forkhandles/result4k/result.kt +++ b/result4k/src/main/kotlin/dev/forkhandles/result4k/result.kt @@ -5,10 +5,35 @@ package dev.forkhandles.result4k /** * A result of a computation that can succeed or fail. */ -sealed class Result +@Suppress("UNCHECKED_CAST") +@JvmInline +value class Result(private val _value: Any?) { + fun isFailure(): Boolean = _value is FailureWrapper<*> + fun isSuccess(): Boolean = !isFailure() -data class Success(val value: T) : Result() -data class Failure(val reason: E) : Result() + @PublishedApi + internal val unsafeValue: T + get() = when { + isSuccess() -> _value as T + else -> error("Attempt to get value on $this") + } + + @PublishedApi + internal val unsafeReason: E + get() = when { + isSuccess() -> error("Attempt to get reason on $this") + else -> (_value as FailureWrapper).reason + } + + internal data class FailureWrapper(internal val reason: F) +} + +fun Success(value: T): Result = Result(value) +fun Failure(failure: E): Result = Result(Result.FailureWrapper(failure)) + +inline val Result.reason: E get() = unsafeReason + +inline val Result.value: T get() = unsafeValue /** * Call a function and wrap the result in a Result, catching any Exception and returning it as Err value. @@ -29,19 +54,22 @@ inline fun Result.map(f: (T) -> Tʹ): Result = /** * Flat-map a function over the _value_ of a successful Result. */ +@Suppress("UNCHECKED_CAST") inline fun Result.flatMap(f: (T) -> Result): Result = - when (this) { - is Success -> f(value) - is Failure -> this + when { + this.isSuccess() -> f(unsafeValue) + else -> this as Result } /** * Flat-map a function over the _reason_ of a unsuccessful Result. */ -inline fun Result.flatMapFailure(f: (E) -> Result): Result = when (this) { - is Success -> this - is Failure -> f(reason) -} +@Suppress("UNCHECKED_CAST") +inline fun Result.flatMapFailure(f: (E) -> Result): Result = + when { + this.isFailure() -> f(unsafeReason) + else -> this as Result + } /** * Map a function over the _reason_ of an unsuccessful Result. @@ -52,25 +80,25 @@ inline fun Result.mapFailure(f: (E) -> Eʹ): Result = /** * Unwrap a Result in which both the success and failure values have the same type, returning a plain value. */ -fun Result.get() = when (this) { - is Success -> value - is Failure -> reason +fun Result.get() = when { + isSuccess() -> unsafeValue + else -> unsafeReason } /** * Unwrap a successful result or throw an exception */ -fun Result.orThrow() = when (this) { - is Success -> value - is Failure -> throw reason +fun Result.orThrow() = when { + this.isSuccess() -> unsafeValue + else -> throw unsafeReason } /** * Unwrap a Result, by returning the success value or calling _block_ on failure to abort from the current function. */ -inline fun Result.onFailure(block: (Failure) -> Nothing): T = when (this) { - is Success -> value - is Failure -> block(this) +inline fun Result.onFailure(block: (Result) -> Nothing): T = when { + this.isSuccess() -> unsafeValue + else -> block(this) } /** @@ -83,11 +111,11 @@ inline fun Result.recover(errorToValue: (E) -> U): S * Perform a side effect with the success value. */ inline fun Result.peek(f: (T) -> Unit) = - apply { if (this is Success) f(value) } + apply { if (this.isSuccess()) f(unsafeValue) } /** * Perform a side effect with the failure reason. */ inline fun Result.peekFailure(f: (E) -> Unit) = - apply { if (this is Failure) f(reason) } + apply { if (this.isFailure()) f(unsafeReason) } diff --git a/values4k/src/main/kotlin/dev/forkhandles/values/result4k.kt b/values4k/src/main/kotlin/dev/forkhandles/values/result4k.kt index 52eb4ca..dd04887 100644 --- a/values4k/src/main/kotlin/dev/forkhandles/values/result4k.kt +++ b/values4k/src/main/kotlin/dev/forkhandles/values/result4k.kt @@ -4,6 +4,7 @@ import dev.forkhandles.result4k.Failure import dev.forkhandles.result4k.Result import dev.forkhandles.result4k.Success import dev.forkhandles.result4k.map +import dev.forkhandles.result4k.recover import dev.forkhandles.result4k.resultFrom /** @@ -35,9 +36,8 @@ private fun , PRIMITIVE : Any> List.toResult4k else -> drop(1) .fold(fn(first()).map(::listOf)) { acc, next -> - when (acc) { - is Success -> fn(next).map { acc.value + it } - is Failure -> acc - } + acc + .map { value -> fn(next).map { value + it }} + .recover { acc } } }