a simple result monad for kotlin
Switch branches/tags
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.

README.md

kotlin-result Jitpack Build Status

A simple Result monad for Kotlin.

Extracted from kog.

Table of Contents

Install

repositories {
    maven { url "https://jitpack.io" }
}

dependencies {
    compile "com.danneu:kotlin-result:x.y.z"
    // Or always get latest
    compile "com.danneu:kotlin-result:master-SNAPSHOT"
}

Usage

A Result represents an outcome of an operation that can succeed or fail.

  • Result.Ok wraps the output value: result.value
  • Result.Err wraps the output error: result.error

The purpose of the abstraction is to provide a more functional and composable way to handle errors than try/catch.

For an example, check out kittinunf/Result#why.

Creating Results

val okResult = Result.ok(42)
val errResult = Result.err("failure")

Unwrapping Results

.getOrThrow()

Force-unwrap a result value.

Result.ok(42).getOrThrow() == 42
Result.err("failure").getOrThrow() // throws com.danneu.result.UnwrapException

.getOrElse()

Unwrap a result value with a fallback in case of error.

Result.ok(42).getOrElse(-1) == 42
Result.err("failure").getOrElse(-1) == -1

Or pass in a function if you want to inspect or transform the error.

Result.err("failure").getOrElse { message ->
    message + "-transformed"
} == "failure-transformed"

Transforming Results

.map()

Transform a result's value.

Result.ok(100).map { it + 1 } == Result.ok(101)
Result.err("failure").map { it + 1 } == Result.err("failure")

.mapError()

Transform a result's error.

Result.ok(100).mapError { it + "-mapped" } == Result.ok(100)
Result.err("failure").mapError { it + "-mapped" } == Result.err("failure-mapped")

.fold()

Reduce both sides into a final value.

Result.ok(100).fold({ it + 1 }, { it + "-mapped" }) == 101
Result.err("failure").fold({ it + 1 }, { -1 }) == -1

.flatMap()

Transform result into a new result based on its value.

Like .map() except that the lambda returns a new result instead of a new value.

Result.ok(42).flatMap { Result.ok(100) } == Result.ok(100)
Result.err("failure").flatMap { Result.ok(100) } == Result.err("failure")

.flatMapError()

Transform result into a new result based on its error.

Like .mapError() except that the lambda returns a new result instead of a new error.

Result.ok(42).flatMapError { Result.ok(100) } == Result.ok(42)
Result.err("failure").flatMapError { Result.ok(100) } == Result.ok(100)

Combining Results

Result.all()

Combine a list of results into a single result.

Short-circuits on first error.

Result.all(ok(1), ok(2), ok(3)) == Result.ok([1, 2, 3])
Result.all(ok(1), err("failure"), ok(3)) == Result.err("failure")

Representing Impossible Failure

To represent a Result that can never fail, use Kotlin's Never:

fun add (a: Int, b: Int): Result<Int, Never> {
    return Result.ok(a + b)
}

Versus kittinunf/Result

kittinunf/Result is another Result monad library implemented for Kotlin. However, it constrains its Result value to non-null values and its error to instances of Exception.

This library does not constrain either types. This means that it can model nullable result values and errors represented as any type, like a simple string error message or an integer error code.

For example, you can represent Result<User?, String> in this library but not in kittinunf/Result.