From a66bdcfd6c87f1c188695462dbbd4e50fc3f3f5e Mon Sep 17 00:00:00 2001 From: Michael Bull Date: Tue, 5 Mar 2024 20:36:03 +0000 Subject: [PATCH] Convert Result to an inline value class --- .../com/github/michaelbull/result/Get.kt | 15 --- .../com/github/michaelbull/result/Result.kt | 127 +++++++----------- .../com/github/michaelbull/result/Unwrap.kt | 10 -- 3 files changed, 51 insertions(+), 101 deletions(-) diff --git a/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Get.kt b/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Get.kt index 815b8e6..535d7ad 100644 --- a/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Get.kt +++ b/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Get.kt @@ -10,11 +10,6 @@ import kotlin.contracts.contract * - Rust: [Result.ok](https://doc.rust-lang.org/std/result/enum.Result.html#method.ok) */ public fun Result.get(): V? { - contract { - returnsNotNull() implies (this@get is Ok) - returns(null) implies (this@get is Err) - } - return when { isOk -> value else -> null @@ -27,11 +22,6 @@ public fun Result.get(): V? { * - Rust: [Result.err](https://doc.rust-lang.org/std/result/enum.Result.html#method.err) */ public fun Result.getError(): E? { - contract { - returns(null) implies (this@getError is Ok) - returnsNotNull() implies (this@getError is Err) - } - return when { isErr -> error else -> null @@ -111,10 +101,6 @@ public inline infix fun Result.getErrorOrElse(transform: (V) -> E): * This is functionally equivalent to [`getOrElse { throw it }`][getOrElse]. */ public fun Result.getOrThrow(): V { - contract { - returns() implies (this@getOrThrow is Ok) - } - return when { isOk -> value else -> throw error @@ -127,7 +113,6 @@ public fun Result.getOrThrow(): V { */ public inline infix fun Result.getOrThrow(transform: (E) -> Throwable): V { contract { - returns() implies (this@getOrThrow is Ok) callsInPlace(transform, InvocationKind.AT_MOST_ONCE) } diff --git a/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Result.kt b/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Result.kt index 9cb3cfa..834105e 100644 --- a/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Result.kt +++ b/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Result.kt @@ -1,19 +1,21 @@ package com.github.michaelbull.result +import kotlin.jvm.JvmInline + /** * Returns a [Result] that [is ok][Result.isOk] and contains a [value][Result.value]. */ -@Suppress("FunctionName", "DEPRECATION") +@Suppress("FunctionName") public fun Ok(value: V): Result { - return Ok(value, null) + return Result(value) } /** * Returns a [Result] that [is an error][Result.isErr] and contains an [error][Result.error]. */ -@Suppress("FunctionName", "DEPRECATION") +@Suppress("FunctionName") public fun Err(error: E): Result { - return Err(error, null) + return Result(Failure(error)) } /** @@ -37,94 +39,67 @@ public inline fun Result.asErr(): Result { /** * [Result] is a type that represents either success ([Ok]) or failure ([Err]). * + * A [Result] that [is ok][Result.isOk] will have a [value][Result.value] of type [V], whereas a + * [Result] that [is an error][Result.isErr] will have an [error][Result.error] of type [E]. + * * - Elm: [Result](http://package.elm-lang.org/packages/elm-lang/core/5.1.1/Result) * - Haskell: [Data.Either](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Either.html) * - Rust: [Result](https://doc.rust-lang.org/std/result/enum.Result.html) */ -public sealed class Result { - - 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? -} - -/** - * Represents a successful [Result], containing a [value]. - */ -@Deprecated( - message = "Using Ok as a return type is deprecated.", - replaceWith = ReplaceWith("Result"), -) -public class Ok internal constructor( - override val value: V, - @Suppress("UNUSED_PARAMETER") placeholder: Any?, -) : Result() { - - override val error: Nothing - get() { - throw NoSuchElementException() - } +@JvmInline +public value class Result internal constructor( + private val inlineValue: Any?, +) { - override val isOk: Boolean = true - override val isErr: Boolean = false + @Suppress("UNCHECKED_CAST") + public val value: V + get() = inlineValue as V - override fun component1(): V = value - override fun component2(): Nothing? = null + @Suppress("UNCHECKED_CAST") + public val error: E + get() = (inlineValue as Failure).error - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || this::class != other::class) return false + public val isOk: Boolean + get() = inlineValue !is Failure<*> - other as Ok<*> + public val isErr: Boolean + get() = inlineValue is Failure<*> - if (value != other.value) return false - - return true + public operator fun component1(): V? { + return when { + isOk -> value + else -> null + } } - override fun hashCode(): Int = value.hashCode() - override fun toString(): String = "Ok($value)" -} - -/** - * Represents a failed [Result], containing an [error]. - */ -@Deprecated( - message = "Using Err as a return type is deprecated.", - replaceWith = ReplaceWith("Result"), -) -public class Err internal constructor( - override val error: E, - @Suppress("UNUSED_PARAMETER") placeholder: Any?, -) : Result() { - - override val value: Nothing - get() { - throw NoSuchElementException() + public operator fun component2(): E? { + return when { + isErr -> error + else -> null } + } - override val isOk: Boolean = false - override val isErr: Boolean = true - - override fun component1(): Nothing? = null - override fun component2(): E = error + override fun toString(): String { + return when { + isOk -> "Ok($value)" + else -> "Err($error)" + } + } +} +private class Failure( + val error: E, +) { override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || this::class != other::class) return false - - other as Err<*> - - if (error != other.error) return false + return other is Failure<*> && error == other.error + } - return true + override fun hashCode(): Int { + return error.hashCode() } - override fun hashCode(): Int = error.hashCode() - override fun toString(): String = "Err($error)" + override fun toString(): String { + return "Failure($error)" + } } + diff --git a/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Unwrap.kt b/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Unwrap.kt index 136b65f..b72389c 100644 --- a/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Unwrap.kt +++ b/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Unwrap.kt @@ -14,10 +14,6 @@ public class UnwrapException(message: String) : Exception(message) * @throws UnwrapException if this result [is an error][Result.isErr]. */ public fun Result.unwrap(): V { - contract { - returns() implies (this@unwrap is Ok) - } - return when { isOk -> value else -> throw UnwrapException("called Result.unwrap on an Err value $error") @@ -38,7 +34,6 @@ public fun Result.unwrap(): V { public inline infix fun Result.expect(message: () -> Any): V { contract { callsInPlace(message, InvocationKind.AT_MOST_ONCE) - returns() implies (this@expect is Ok) } return when { @@ -56,10 +51,6 @@ public inline infix fun Result.expect(message: () -> Any): V { * @throws UnwrapException if this result [is ok][Result.isOk]. */ public fun Result.unwrapError(): E { - contract { - returns() implies (this@unwrapError is Err) - } - return when { isErr -> error else -> throw UnwrapException("called Result.unwrapError on an Ok value $value") @@ -80,7 +71,6 @@ public fun Result.unwrapError(): E { public inline infix fun Result.expectError(message: () -> Any): E { contract { callsInPlace(message, InvocationKind.AT_MOST_ONCE) - returns() implies (this@expectError is Err) } return when {