Last active
June 16, 2020 17:04
-
-
Save elizarov/03425be1c4209d59ad813ddccab29313 to your computer and use it in GitHub Desktop.
Result for Kotlin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Result<T> private constructor(private val result: Any?) { | |
// discovery | |
val isFailure: Boolean get() = result is Failure | |
val isSuccess: Boolean get() = result !is Failure | |
// value retrieval | |
fun get(): T = | |
if (result is Failure) throw result.exception | |
else result as T | |
fun getOrNull(): T? = | |
if (result is Failure) null | |
else result as T | |
inline fun getOrElse(default: () -> T): T = | |
if (isFailure) default() | |
else value | |
// exception retrieval | |
fun exceptionOrNull(): Throwable? = | |
if (result is Failure) result.exception | |
else null | |
// companion with constructors | |
companion object { | |
fun <T> success(value: T): Result<T> = Result(value) | |
fun <T> failure(exception: Throwable) = Result<T>(Failure(exception)) | |
} | |
// internal API for inline functions | |
@PublishedApi internal val exception: Throwable get() = (result as Failure).exception | |
@PublishedApi internal val value: T get() = result as T | |
private class Failure(@JvmField val exception: Throwable) | |
} | |
inline fun <T> resultOf(block: () -> T): Result<T> = | |
try { | |
Result.success(block()) | |
} | |
catch (e: Throwable) { | |
Result.failure(e) | |
} | |
// -- extensions --- | |
// transformation | |
inline fun <U, T> Result<T>.map(block: (T) -> U): Result<U> = | |
if (isFailure) this as Result<U> | |
else resultOf { block(value) } | |
inline fun <U, T: U> Result<T>.handle(block: (Throwable) -> U): Result<U> = | |
if (isFailure) resultOf { block(exception) } | |
else this as Result<U> | |
// "peek" onto value/exception and pipe | |
inline fun <T> Result<T>.onFailure(block: (Throwable) -> Unit): Result<T> { | |
if (isFailure) block(exception) | |
return this | |
} | |
inline fun <T> Result<T>.onSuccess(block: (T) -> Unit): Result<T> { | |
if (isSuccess) block(value) | |
return this | |
} | |
// ------------------- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello, Roman.
Here is the gist of my recent Result implementation. It's basically very similar to your implementation. Actually, I've taken what i consider the best of it. Yet there are some nuances I'd like to discuss.
Result
explicitly withThrowable
as failed and withvalue: T
as successful. Wherein leaving companion constructors for decoration purposes and for the rare case when the typeT
isThrowable
.unsafeValue: T
andunsafeException: Throwable
(value
andexception
variables respectively in your implementation) is useful for creating custom extension functions of course with a due focus on the unsafety which can be stressed by 'unsafe' prefix or something like that.get...()
is transited from similarity with theOptional
. However,Result
is slightly different. While inOptional
user expects a value or nothing, inResult
one expects a value or an exception. And that is internally reflected in your implementation too (correspondingvalue
andexception
variables )Eventually, i hope to see your implementation in the Kotlin standard library. And I hope you might find something useful in my implementation as well.