Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/zalando/faux-pas
A library that simplifies error handling for Functional Programming in Java
https://github.com/zalando/faux-pas
completable-future error-handling exception-handling functional-programming java monitoring
Last synced: 2 months ago
JSON representation
A library that simplifies error handling for Functional Programming in Java
- Host: GitHub
- URL: https://github.com/zalando/faux-pas
- Owner: zalando
- License: mit
- Created: 2016-08-24T06:01:34.000Z (over 8 years ago)
- Default Branch: main
- Last Pushed: 2023-06-13T04:58:41.000Z (over 1 year ago)
- Last Synced: 2024-08-01T18:24:50.242Z (5 months ago)
- Topics: completable-future, error-handling, exception-handling, functional-programming, java, monitoring
- Language: Java
- Homepage:
- Size: 590 KB
- Stars: 130
- Watchers: 13
- Forks: 11
- Open Issues: 10
-
Metadata Files:
- Readme: README.md
- Contributing: .github/CONTRIBUTING.md
- License: LICENSE
- Code of conduct: .github/CODE_OF_CONDUCT.md
- Codeowners: .github/CODEOWNERS
- Security: SECURITY.md
Awesome Lists containing this project
- awesome-java - Faux Pas - Library that simplifies error handling by circumventing the issue that none of the functional interfaces in the Java Runtime is allowed by default to throw checked exceptions. (Projects / Development)
- awesome-java-zh - Faux Pas - 库,通过规避默认情况下不允许Java运行时中的任何函数式接口抛出检查异常的问题,简化了错误处理。 (项目 / 发展)
- awesome-java - Faux Pas - Library that simplifies error handling by circumventing the issue that none of the functional interfaces in the Java Runtime is allowed by default to throw checked exceptions. (Projects / Development)
README
# Faux Pas: Error handling in Functional Programming
[![Spilled coffee](docs/spilled-coffee.jpg)](https://pixabay.com/en/mistake-spill-slip-up-accident-876597/)
[![Stability: Sustained](https://masterminds.github.io/stability/sustained.svg)](https://masterminds.github.io/stability/sustained.html)
![Build Status](https://github.com/zalando/faux-pas/workflows/build/badge.svg)
[![Coverage Status](https://img.shields.io/coveralls/zalando/faux-pas/main.svg)](https://coveralls.io/r/zalando/faux-pas)
[![Code Quality](https://img.shields.io/codacy/grade/b3a619ff47574eb68f38bdf74906e91a/main.svg)](https://www.codacy.com/app/whiskeysierra/faux-pas)
[![Javadoc](http://javadoc.io/badge/org.zalando/faux-pas.svg)](http://www.javadoc.io/doc/org.zalando/faux-pas)
[![Release](https://img.shields.io/github/release/zalando/faux-pas.svg)](https://github.com/zalando/faux-pas/releases)
[![Maven Central](https://img.shields.io/maven-central/v/org.zalando/faux-pas.svg)](https://maven-badges.herokuapp.com/maven-central/org.zalando/faux-pas)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/zalando/faux-pas/main/LICENSE)> **Faux pas** *noun*, /fəʊ pɑː/: blunder; misstep, false step
_**F**aux **P**as_ is a library that simplifies error handling for **F**unctional **P**rogramming in Java. It fixes the
issue that none of the functional interfaces in the Java Runtime by default is allowed to throw checked exceptions.- **Technology stack**: Java 8+, functional interfaces
- **Status**: 0.x, originally ported from [Riptide](https://www.github.com/zalando/riptide), used in production## Example
```java
interface Client {
User read(final String name) throws IOException;
}Function readUser = throwingFunction(client::read);
readUser.apply("Bob"); // may throw IOException directly
```## Features
- Checked exceptions for functional interfaces
- Compatible with the JDK types## Dependencies
- Java 8 or higher
- Lombok (no runtime dependency)## Installation
Add the following dependency to your project:
```xml
org.zalando
faux-pas
${faux-pas.version}```
## Usage
### Throwing functional interfaces
*Faux Pas* has a variant of every major functional interface from the Java core:
- [`ThrowingRunnable`](src/main/java/org/zalando/fauxpas/ThrowingRunnable.java)
- [`ThrowingSupplier`](src/main/java/org/zalando/fauxpas/ThrowingSupplier.java)
- [`ThrowingConsumer`](src/main/java/org/zalando/fauxpas/ThrowingConsumer.java)
- [`ThrowingFunction`](src/main/java/org/zalando/fauxpas/ThrowingFunction.java)
- [`ThrowingUnaryOperator`](src/main/java/org/zalando/fauxpas/ThrowingUnaryOperator.java)
- [`ThrowingPredicate`](src/main/java/org/zalando/fauxpas/ThrowingPredicate.java)
- [`ThrowingBiConsumer`](src/main/java/org/zalando/fauxpas/ThrowingBiConsumer.java)
- [`ThrowingBiFunction`](src/main/java/org/zalando/fauxpas/ThrowingBiFunction.java)
- [`ThrowingBinaryOperator`](src/main/java/org/zalando/fauxpas/ThrowingBinaryOperator.java)
- [`ThrowingBiPredicate`](src/main/java/org/zalando/fauxpas/ThrowingBiPredicate.java)The followings statements apply to each of them:
- extends the official interface, i.e. they are 100% compatible
- [*sneakily throws*](https://projectlombok.org/features/SneakyThrows.html) the original exception#### Creation
The way the Java runtime implemented functional interfaces always requires additional type information, either by
using a cast or a local variable:```java
// compiler error
client::read.apply(name);// too verbose
((ThrowingFunction) client::read).apply(name);// local variable may not always be desired
ThrowingFunction readUser = client::read;
readUser.apply(name);
```As a workaround there is a static *factory* method for every interface type in`FauxPas`. All of them are called
`throwingRunnable`, `throwingSupplier` and so forth. It allows for concise one-line statements:```java
List users = names.stream()
.map(throwingFunction(client::read))
.collect(toList());
```### Try-with-resources alternative
Traditional `try-with-resources` statements are compiled into byte code that includes
[unreachable parts](http://stackoverflow.com/a/17356707) and unfortunately JaCoCo has no
[support for filtering](https://github.com/jacoco/jacoco/wiki/FilteringOptions) yet. That's why we came up with an
alternative implementation. The [official example](https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html)
for the `try-with-resources` statement looks like this:```java
try (BufferedReader br =
new BufferedReader(new FileReader(path))) {
return br.readLine();
}
```Compared to ours:
```java
return tryWith(new BufferedReader(new FileReader(path)), br ->
br.readLine()
);
```### CompletableFuture.exceptionally(Function)
[`CompletableFuture.exceptionally(..)`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.function.Function-)
is a very powerful but often overlooked tool. It allows to inject [partial exception handling](https://stackoverflow.com/questions/37032990/separated-exception-handling-of-a-completablefuture)
into a `CompletableFuture`:```java
future.exceptionally(e -> {
Throwable t = e instanceof CompletionException ? e.getCause() : e;if (t instanceof NoRouteToHostException) {
return fallbackValueFor(e);
}throw e instanceof CompletionException ? e : new CompletionException(t);
})
```Unfortunately it has a contract that makes it harder to use than it needs to:
- It takes a [`Throwable`](https://docs.oracle.com/javase/8/docs/api/java/lang/Throwable.html) as an argument, but
doesn't allow to re-throw it *as-is*. This can be circumvented by optionally [wrapping it in a
`CompletionException`](http://cs.oswego.edu/pipermail/concurrency-interest/2014-August/012910.html) before
rethrowing it.
- The throwable argument is [sometimes wrapped](https://stackoverflow.com/questions/27430255/surprising-behavior-of-java-8-completablefuture-exceptionally-method)
inside a [`CompletionException`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionException.html)
and sometimes it's not, depending on whether there is any other computation step before the `exceptionally(..)` call
or not.
In order to use the operation correctly one needs to follow these rules:
1. Unwrap given throwable if it's an instance of `CompletionException`.
2. Wrap checked exceptions in a `CompletionException` before throwing.`FauxPas.partially(..)` relives some of the pain by changing the interface and contract a bit to make it more usable.
The following example is functionally equivalent to the one from above:```java
future.exceptionally(partially(e -> {
if (e instanceof NoRouteToHostException) {
return fallbackValueFor(e);
}throw e;
}))
```1. Takes a `ThrowingFunction`, i.e. it allows clients to
- directly re-throw the throwable argument
- throw any exception during exception handling *as-is*
2. Will automatically unwrap a `CompletionException` before passing it to the given function.
I.e. the supplied function will never have to deal with `CompletionException` directly. Except for the rare occasion
that the `CompletionException` has no cause, in which case it will be passed to the given function.
3. Will automatically wrap any thrown `Exception` inside a `CompletionException`, if needed.The last example is actually so common, that there is an overloaded version of `partially` that caters for this use
particular case:```java
future.exceptionally(partially(NoRouteToHostException.class, this::fallbackValueFor))
```### CompletableFuture.whenComplete(BiConsumer)
```java
future.whenComplete(failedWith(TimeoutException.class, e -> {
request.cancel();
}))
```Other missing pieces in `CompletableFuture`'s API are `exceptionallyCompose` and `handleCompose`. Both can be seen as
a combination of `exceptionally` + `compose` and `handle` + `compose` respectively. They basically allow to supply
another `CompletableFuture` rather than concrete values directly. This is allows for asynchronous fallbacks:```java
exceptionallyCompose(users.find(name), e -> archive.find(name))
```## Getting Help
If you have questions, concerns, bug reports, etc., please file an issue in this repository's [Issue Tracker](../../issues).
## Getting Involved/Contributing
To contribute, simply make a pull request and add a brief description (1-2 sentences) of your addition or change. For
more details, check the [contribution guidelines](.github/CONTRIBUTING.md).## Alternatives
- [Lombok's `@SneakyThrows`](https://projectlombok.org/features/SneakyThrows.html)
- [Durian's Errors](https://github.com/diffplug/durian)
- [Spotify's Completable Futures](https://github.com/spotify/completable-futures)