Skip to content

Instantly share code, notes, and snippets.

@dam5s
Last active June 22, 2022 21:28
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dam5s/7fad877656fa891640c115688dbe0f5a to your computer and use it in GitHub Desktop.
Save dam5s/7fad877656fa891640c115688dbe0f5a to your computer and use it in GitHub Desktop.
Railway oriented programming in Kotlin - This is code accompanying my blog post https://medium.com/@its_damo/error-handling-in-kotlin-a07c2ee0e06f
sealed class Result<A, E> {
fun <B> map(mapping: (A) -> B): Result<B, E> =
when (this) {
is Success -> Success(mapping(value))
is Failure -> Failure(reason)
}
fun <B> bind(mapping: (A) -> Result<B, E>): Result<B, E> =
when (this) {
is Success -> mapping(value)
is Failure -> Failure(reason)
}
fun <F> mapFailure(mapping: (E) -> F): Result<A, F> =
when (this) {
is Success -> Success(value)
is Failure -> Failure(mapping(reason))
}
fun bindFailure(mapping: (E) -> Result<A, E>): Result<A, E> =
when (this) {
is Success -> Success(value)
is Failure -> mapping(reason)
}
fun orElse(other: A): A =
when (this) {
is Success -> value
is Failure -> other
}
fun orElse(function: (E) -> A): A =
when (this) {
is Success -> value
is Failure -> function(reason)
}
fun orNull(): A? =
when (this) {
is Success -> value
is Failure -> null
}
}
data class Success<A, E>(val value: A) : Result<A, E>()
data class Failure<A, E>(val reason: E) : Result<A, E>()
@kashzonal
Copy link

kashzonal commented Feb 21, 2020

Nice blog post. I think it could be improved by doing this:

sealed class Result<out T, out Error>
data class Success<T>(val value: T): Result<T, Nothing>()
data class Failure<Error>(val error: Error): Result<Nothing, Error>()

fun <T> Result<T, *>.orElse(other: T) = if (this is Success) value else other
fun <C,T> Result<T, *>.map(f: (T) -> C) = if (this is Success) Success(f(value)) else this
fun <C,T> Result<T, *>.flatMap(f: (T) -> Result<C, *>) = if (this is Success) f(value) else this
fun <E,C> Result<*, E>.mapFailure(f: (E) -> C) = if (this is Failure) Failure(f(error)) else this

fun <T,E> Result<T,E>.orElse(f: (E) -> T) = when (this) {
    is Success -> value
    is Failure -> f(error)
}

@dam5s
Copy link
Author

dam5s commented Feb 24, 2020

Updated with more readable version.

@aleksandar78
Copy link

Very nice code example. I'm using Kotlin for the server-side project and I'm of the idea that Exception is fine for "exceptional" error propagation but in 99% of cases it becomes control flow, and that's not good.
Railway error handling is a very nice pattern and I'll take this code sample as a prototype for our internal lib.

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