Skip to content

bgerstle/result-java

Repository files navigation

badget

Result

A simple Result type in Java for functional exception handling. Inspired by Haskell's Either, antitypical/Result in Swift, and cyclops' Try.

Why should I use it?

TL;DR; Result allows you to call methods that throw checked exceptions in lambas without messy workarounds like nested try/catch or runtime exceptions.

With the advent of functional APIs in Java such as Optional and Stream, it's not uncommon to call methods which can throw a checked (or unchecked) exception in your lambdas. For instance, when parsing a URL from the app's environment:

Optional<URI> apiBaseURL = Optional.ofNullable(System.getenv("apiBaseURL")).map(baseUrlString -> {
  // Compiler error, unhandled URISyntaxException
  return new URI(baseUrlString);
});

In order to handle that URISyntaxException and unpack the optional, you might wind up with code looking like this:

URI apiBaseURL;
try {
  apiBaseURL = Optional
      .ofNullable(System.getenv("apiBaseURL"))
      .map(baseUrlString -> {
        try {
          return new URI(baseUrlString);
        } catch (URISyntaxException e) {
          // wrap as unchecked exception, catch outside of lambda
          throw new RuntimeException("uri parsing error", e);
        }
      })
      .get();
} catch (NoSuchElementException e) {
  throw new IllegalStateException("Missing apiBaseURL env var");
} catch (RuntimeException e) {
  // catch and re-throw the wrapped URISyntaxException
  if (e.getCause() instanceof URISyntaxException) {
    throw (URISyntaxException)e.getCause();
  } else {
    // something else weird happened, rethrow
    throw e;
  }
}

Variable reassignment, nested try/catch blocks, and runtime exceptions—oh my! Let's clean this up using Result:

URI apiBaseURI = Result
  .<String, Exception>attempt(Optional.ofNullable(System.getenv("apiBaseURL")::get) // 1
  .flatMap(Result.from(URI::new)) // 2
  .orElseThrow(); // 3

Here's the play by play:

  1. attempt to get the optional value of System.getenv(...), this yields a Result<String, Exception> which either contains a String or an exception (in this case, a NoSuchElementException)
  2. flatMap the initial Result by trying to construct a URI, which returns a Result<URI, Exception>, containing either a URI or an exception
  3. Unpack the final Result, which will either throw one of the captured exceptions or return the transformed value

Other great use cases for Result include:

  • Rethrowing exceptions as an assertion: Result.attempt(...).orElseAssert() (similar to try! in Swift)
  • Returning an empty optional on error: Result<T, E>.attempt(...).getValue() // Optional<T> (similar to try? in Swift)

How do I use it?

Result isn't published in any repositories, so Jitpack is probably your best bet:

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

dependencies {
    ...
    compile 'com.github.bgerstle:result-java:master-SNAPSHOT'
}

You can substitute a tag or commit with master-SNAPSHOT if you don't to be on master.

How do I build it?

With Java 11 or above installed, from the command line:

./gradlew assemble

How do I test it?

With Java 11 or above installed, from the command line:

./gradlew check -i

Or, run all the tests in IntelliJ using a JUnit Run Configuration.

About

A simple Result type in Java for functional exception handling.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy