Skip to content

Instantly share code, notes, and snippets.

@elizarov
Last active June 16, 2020 17:04
Show Gist options
  • Select an option

  • Save elizarov/03425be1c4209d59ad813ddccab29313 to your computer and use it in GitHub Desktop.

Select an option

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

just-4-fun commented May 9, 2018

Copy link
Copy Markdown

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