Skip to content

Instantly share code, notes, and snippets.

@elizarov
Last active June 16, 2020 17:04
Show Gist options
  • Save elizarov/03425be1c4209d59ad813ddccab29313 to your computer and use it in GitHub Desktop.
Save elizarov/03425be1c4209d59ad813ddccab29313 to your computer and use it in GitHub Desktop.
Result for Kotlin
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
}
// -------------------
@just-4-fun
Copy link

just-4-fun commented May 9, 2018

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.

  • It seems practical and convenient to let construct Result explicitly with Throwable as failed and with value: T as successful. Wherein leaving companion constructors for decoration purposes and for the rare case when the type T is Throwable.
  • The public access to unsafeValue: T and unsafeException: Throwable (value and exception 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.
  • I guess the naming convention of get...() is transited from similarity with the Optional. However, Result is slightly different. While in Optional user expects a value or nothing, in Result one expects a value or an exception. And that is internally reflected in your implementation too (corresponding value and exception 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment