Skip to content

Commit 981fbe2

Browse files
committed
Convert Result to an inline value class
1 parent eecd1b7 commit 981fbe2

File tree

3 files changed

+50
-101
lines changed

3 files changed

+50
-101
lines changed

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

-15
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,6 @@ import kotlin.contracts.contract
1010
* - Rust: [Result.ok](https://doc.rust-lang.org/std/result/enum.Result.html#method.ok)
1111
*/
1212
public fun <V, E> Result<V, E>.get(): V? {
13-
contract {
14-
returnsNotNull() implies (this@get is Ok<V>)
15-
returns(null) implies (this@get is Err<E>)
16-
}
17-
1813
return when {
1914
isOk -> value
2015
else -> null
@@ -27,11 +22,6 @@ public fun <V, E> Result<V, E>.get(): V? {
2722
* - Rust: [Result.err](https://doc.rust-lang.org/std/result/enum.Result.html#method.err)
2823
*/
2924
public fun <V, E> Result<V, E>.getError(): E? {
30-
contract {
31-
returns(null) implies (this@getError is Ok<V>)
32-
returnsNotNull() implies (this@getError is Err<E>)
33-
}
34-
3525
return when {
3626
isErr -> error
3727
else -> null
@@ -111,10 +101,6 @@ public inline infix fun <V, E> Result<V, E>.getErrorOrElse(transform: (V) -> E):
111101
* This is functionally equivalent to [`getOrElse { throw it }`][getOrElse].
112102
*/
113103
public fun <V, E : Throwable> Result<V, E>.getOrThrow(): V {
114-
contract {
115-
returns() implies (this@getOrThrow is Ok<V>)
116-
}
117-
118104
return when {
119105
isOk -> value
120106
else -> throw error
@@ -127,7 +113,6 @@ public fun <V, E : Throwable> Result<V, E>.getOrThrow(): V {
127113
*/
128114
public inline infix fun <V, E> Result<V, E>.getOrThrow(transform: (E) -> Throwable): V {
129115
contract {
130-
returns() implies (this@getOrThrow is Ok<V>)
131116
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
132117
}
133118

Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
package com.github.michaelbull.result
22

3+
import kotlin.jvm.JvmInline
4+
35
/**
46
* Returns a [Result] that [is ok][Result.isOk] and contains a [value][Result.value].
57
*/
6-
@Suppress("FunctionName", "DEPRECATION")
8+
@Suppress("FunctionName")
79
public fun <V> Ok(value: V): Result<V, Nothing> {
8-
return Ok(value, null)
10+
return Result(value)
911
}
1012

1113
/**
1214
* Returns a [Result] that [is an error][Result.isErr] and contains an [error][Result.error].
1315
*/
14-
@Suppress("FunctionName", "DEPRECATION")
16+
@Suppress("FunctionName")
1517
public fun <E> Err(error: E): Result<Nothing, E> {
16-
return Err(error, null)
18+
return Result(Failure(error))
1719
}
1820

1921
/**
@@ -37,94 +39,66 @@ public inline fun <V, E, F> Result<V, E>.asErr(): Result<Nothing, F> {
3739
/**
3840
* [Result] is a type that represents either success ([Ok]) or failure ([Err]).
3941
*
42+
* A [Result] that [is ok][Result.isOk] will have a [value][Result.value] of type [V], whereas a
43+
* [Result] that [is an error][Result.isErr] will have an [error][Result.error] of type [E].
44+
*
4045
* - Elm: [Result](http://package.elm-lang.org/packages/elm-lang/core/5.1.1/Result)
4146
* - Haskell: [Data.Either](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Either.html)
4247
* - Rust: [Result](https://doc.rust-lang.org/std/result/enum.Result.html)
4348
*/
44-
public sealed class Result<out V, out E> {
49+
@JvmInline
50+
public value class Result<out V, out E> internal constructor(
51+
private val inlineValue: Any?,
52+
) {
4553

46-
public abstract val value: V
47-
public abstract val error: E
54+
@Suppress("UNCHECKED_CAST")
55+
public val value: V
56+
get() = inlineValue as V
4857

49-
public abstract val isOk: Boolean
50-
public abstract val isErr: Boolean
51-
52-
public abstract operator fun component1(): V?
53-
public abstract operator fun component2(): E?
54-
}
58+
@Suppress("UNCHECKED_CAST")
59+
public val error: E
60+
get() = (inlineValue as Failure<E>).error
5561

56-
/**
57-
* Represents a successful [Result], containing a [value].
58-
*/
59-
@Deprecated(
60-
message = "Using Ok as a return type is deprecated.",
61-
replaceWith = ReplaceWith("Result<V, Nothing>"),
62-
)
63-
public class Ok<out V> internal constructor(
64-
override val value: V,
65-
@Suppress("UNUSED_PARAMETER") placeholder: Any?,
66-
) : Result<V, Nothing>() {
67-
68-
override val error: Nothing
69-
get() {
70-
throw NoSuchElementException()
71-
}
62+
public val isOk: Boolean
63+
get() = inlineValue !is Failure<*>
7264

73-
override val isOk: Boolean = true
74-
override val isErr: Boolean = false
65+
public val isErr: Boolean
66+
get() = inlineValue is Failure<*>
7567

76-
override fun component1(): V = value
77-
override fun component2(): Nothing? = null
78-
79-
override fun equals(other: Any?): Boolean {
80-
if (this === other) return true
81-
if (other == null || this::class != other::class) return false
82-
83-
other as Ok<*>
84-
85-
if (value != other.value) return false
86-
87-
return true
68+
public operator fun component1(): V? {
69+
return when {
70+
isOk -> value
71+
else -> null
72+
}
8873
}
8974

90-
override fun hashCode(): Int = value.hashCode()
91-
override fun toString(): String = "Ok($value)"
92-
}
93-
94-
/**
95-
* Represents a failed [Result], containing an [error].
96-
*/
97-
@Deprecated(
98-
message = "Using Err as a return type is deprecated.",
99-
replaceWith = ReplaceWith("Result<Nothing, E>"),
100-
)
101-
public class Err<out E> internal constructor(
102-
override val error: E,
103-
@Suppress("UNUSED_PARAMETER") placeholder: Any?,
104-
) : Result<Nothing, E>() {
105-
106-
override val value: Nothing
107-
get() {
108-
throw NoSuchElementException()
75+
public operator fun component2(): E? {
76+
return when {
77+
isErr -> error
78+
else -> null
10979
}
80+
}
11081

111-
override val isOk: Boolean = false
112-
override val isErr: Boolean = true
113-
114-
override fun component1(): Nothing? = null
115-
override fun component2(): E = error
82+
override fun toString(): String {
83+
return when {
84+
isOk -> "Ok($value)"
85+
else -> "Err($error)"
86+
}
87+
}
88+
}
11689

90+
private class Failure<out E>(
91+
val error: E,
92+
) {
11793
override fun equals(other: Any?): Boolean {
118-
if (this === other) return true
119-
if (other == null || this::class != other::class) return false
120-
121-
other as Err<*>
122-
123-
if (error != other.error) return false
94+
return other is Failure<*> && error == other.error
95+
}
12496

125-
return true
97+
override fun hashCode(): Int {
98+
return error.hashCode()
12699
}
127100

128-
override fun hashCode(): Int = error.hashCode()
129-
override fun toString(): String = "Err($error)"
101+
override fun toString(): String {
102+
return "Failure($error)"
103+
}
130104
}

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

-10
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ public class UnwrapException(message: String) : Exception(message)
1414
* @throws UnwrapException if this result [is an error][Result.isErr].
1515
*/
1616
public fun <V, E> Result<V, E>.unwrap(): V {
17-
contract {
18-
returns() implies (this@unwrap is Ok<V>)
19-
}
20-
2117
return when {
2218
isOk -> value
2319
else -> throw UnwrapException("called Result.unwrap on an Err value $error")
@@ -38,7 +34,6 @@ public fun <V, E> Result<V, E>.unwrap(): V {
3834
public inline infix fun <V, E> Result<V, E>.expect(message: () -> Any): V {
3935
contract {
4036
callsInPlace(message, InvocationKind.AT_MOST_ONCE)
41-
returns() implies (this@expect is Ok<V>)
4237
}
4338

4439
return when {
@@ -56,10 +51,6 @@ public inline infix fun <V, E> Result<V, E>.expect(message: () -> Any): V {
5651
* @throws UnwrapException if this result [is ok][Result.isOk].
5752
*/
5853
public fun <V, E> Result<V, E>.unwrapError(): E {
59-
contract {
60-
returns() implies (this@unwrapError is Err<E>)
61-
}
62-
6354
return when {
6455
isErr -> error
6556
else -> throw UnwrapException("called Result.unwrapError on an Ok value $value")
@@ -80,7 +71,6 @@ public fun <V, E> Result<V, E>.unwrapError(): E {
8071
public inline infix fun <V, E> Result<V, E>.expectError(message: () -> Any): E {
8172
contract {
8273
callsInPlace(message, InvocationKind.AT_MOST_ONCE)
83-
returns() implies (this@expectError is Err<E>)
8474
}
8575

8676
return when {

0 commit comments

Comments
 (0)