Faux Pas alternatives and similar libraries
Based on the "Functional Programming" category.
Alternatively, view Faux Pas alternatives based on common mentions on social networks and blogs.
-
Javaslang
vʌvr (formerly called Javaslang) is a non-commercial, non-profit object-functional library that runs with Java 8+. It aims to reduce the lines of code and increase code quality. -
jOOλ
jOOλ - The Missing Parts in Java 8 jOOλ improves the JDK libraries in areas where the Expert Group's focus was elsewhere. It adds tuple support, function support, and a lot of additional functionality around sequential Streams. The JDK 8's main efforts (default methods, lambdas, and the Stream API) were focused around maintaining backwards compatibility and implementing a functional API for parallelism. -
derive4j
Java 8 annotation processor and framework for deriving algebraic data types constructors, pattern-matching, folds, optics and typeclasses. -
Vavr
Functional component library that provides persistent data types and functional control structures.
CodeRabbit: AI Code Reviews for Developers

* Code Quality Rankings and insights are calculated and provided by Lumnify.
They vary from L1 to L5 with "L5" being the highest.
Do you think we are missing an alternative of Faux Pas or a related project?
README
Faux Pas: Error handling in Functional Programming
[Spilled coffee](docs/spilled-coffee.jpg)
Faux pas noun, /fəʊ pɑː/: blunder; misstep, false step
Faux Pas is a library that simplifies error handling for Functional Programming 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, used in production
Example
interface Client {
User read(final String name) throws IOException;
}
Function<String, User> 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:
<dependency>
<groupId>org.zalando</groupId>
<artifactId>faux-pas</artifactId>
<version>${faux-pas.version}</version>
</dependency>
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 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:
// compiler error
client::read.apply(name);
// too verbose
((ThrowingFunction<String, User, IOException>) client::read).apply(name);
// local variable may not always be desired
ThrowingFunction<String, User, IOException> readUser = client::read;
readUser.apply(name);
As a workaround there is a static factory method for every interface type inFauxPas
. All of them are called
throwingRunnable
, throwingSupplier
and so forth. It allows for concise one-line statements:
List<User> 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 and unfortunately JaCoCo has no
support for filtering yet. That's why we came up with an
alternative implementation. The official example
for the try-with-resources
statement looks like this:
try (BufferedReader br =
new BufferedReader(new FileReader(path))) {
return br.readLine();
}
Compared to ours:
return tryWith(new BufferedReader(new FileReader(path)), br ->
br.readLine()
);
CompletableFuture.exceptionally(Function)
CompletableFuture.exceptionally(..)
is a very powerful but often overlooked tool. It allows to inject partial exception handling
into a CompletableFuture
:
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
as an argument, but doesn't allow to re-throw it as-is. This can be circumvented by optionally wrapping it in aCompletionException
before rethrowing it. - The throwable argument is sometimes wrapped
inside a
CompletionException
and sometimes it's not, depending on whether there is any other computation step before theexceptionally(..)
call or not.
In order to use the operation correctly one needs to follow these rules:
- Unwrap given throwable if it's an instance of
CompletionException
. - 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:
future.exceptionally(partially(e -> {
if (e instanceof NoRouteToHostException) {
return fallbackValueFor(e);
}
throw e;
}))
- Takes a
ThrowingFunction<Throwable, T, Throwable>
, i.e. it allows clients to- directly re-throw the throwable argument
- throw any exception during exception handling as-is
- Will automatically unwrap a
CompletionException
before passing it to the given function. I.e. the supplied function will never have to deal withCompletionException
directly. Except for the rare occasion that theCompletionException
has no cause, in which case it will be passed to the given function. - Will automatically wrap any thrown
Exception
inside aCompletionException
, if needed.
The last example is actually so common, that there is an overloaded version of partially
that caters for this use
particular case:
future.exceptionally(partially(NoRouteToHostException.class, this::fallbackValueFor))
CompletableFuture.whenComplete(BiConsumer)
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:
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
*Note that all licence references and agreements mentioned in the Faux Pas README section above
are relevant to that project's source code only.