{"id":13485746,"url":"https://github.com/zalando/faux-pas","last_synced_at":"2025-03-27T19:31:44.884Z","repository":{"id":10634925,"uuid":"66434769","full_name":"zalando/faux-pas","owner":"zalando","description":"A library that simplifies error handling for Functional Programming in Java","archived":false,"fork":false,"pushed_at":"2025-03-17T17:52:57.000Z","size":604,"stargazers_count":137,"open_issues_count":10,"forks_count":12,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-03-17T18:49:23.158Z","etag":null,"topics":["completable-future","error-handling","exception-handling","functional-programming","java","monitoring"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zalando.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null}},"created_at":"2016-08-24T06:01:34.000Z","updated_at":"2025-03-17T17:53:02.000Z","dependencies_parsed_at":"2023-02-19T08:45:28.080Z","dependency_job_id":"cef92c49-f94d-4a63-82a5-712fd79ab7e4","html_url":"https://github.com/zalando/faux-pas","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zalando%2Ffaux-pas","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zalando%2Ffaux-pas/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zalando%2Ffaux-pas/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zalando%2Ffaux-pas/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zalando","download_url":"https://codeload.github.com/zalando/faux-pas/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245910901,"owners_count":20692515,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["completable-future","error-handling","exception-handling","functional-programming","java","monitoring"],"created_at":"2024-07-31T18:00:31.067Z","updated_at":"2025-03-27T19:31:44.495Z","avatar_url":"https://github.com/zalando.png","language":"Java","funding_links":[],"categories":["Projects","Java","项目"],"sub_categories":["Development","发展"],"readme":"# Faux Pas: Error handling in Functional Programming\n\n[![Spilled coffee](docs/spilled-coffee.jpg)](https://pixabay.com/en/mistake-spill-slip-up-accident-876597/)\n\n[![Stability: Sustained](https://masterminds.github.io/stability/sustained.svg)](https://masterminds.github.io/stability/sustained.html)\n![Build Status](https://github.com/zalando/faux-pas/workflows/build/badge.svg)\n[![Coverage Status](https://img.shields.io/coveralls/zalando/faux-pas/main.svg)](https://coveralls.io/r/zalando/faux-pas)\n[![Code Quality](https://img.shields.io/codacy/grade/b3a619ff47574eb68f38bdf74906e91a/main.svg)](https://www.codacy.com/app/whiskeysierra/faux-pas)\n[![Javadoc](http://javadoc.io/badge/org.zalando/faux-pas.svg)](http://www.javadoc.io/doc/org.zalando/faux-pas)\n[![Release](https://img.shields.io/github/release/zalando/faux-pas.svg)](https://github.com/zalando/faux-pas/releases)\n[![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)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/zalando/faux-pas/main/LICENSE)\n\n\u003e **Faux pas** *noun*, /fəʊ pɑː/: blunder; misstep, false step\n\n_**F**aux  **P**as_ is a library that simplifies error handling for **F**unctional **P**rogramming in Java. It fixes the\nissue that none of the functional interfaces in the Java Runtime by default is allowed to throw checked exceptions.\n\n- **Technology stack**: Java 8+, functional interfaces\n- **Status**:  0.x, originally ported from [Riptide](https://www.github.com/zalando/riptide), used in production\n\n## Example\n\n```java\ninterface Client {\n    User read(final String name) throws IOException;\n}\n\nFunction\u003cString, User\u003e readUser = throwingFunction(client::read);\nreadUser.apply(\"Bob\"); // may throw IOException directly\n```\n\n## Features\n\n- Checked exceptions for functional interfaces \n- Compatible with the JDK types\n\n## Dependencies\n\n- Java 8 or higher\n- Lombok (no runtime dependency)\n\n## Installation\n\nAdd the following dependency to your project:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.zalando\u003c/groupId\u003e\n    \u003cartifactId\u003efaux-pas\u003c/artifactId\u003e\n    \u003cversion\u003e${faux-pas.version}\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## Usage\n\n### Throwing functional interfaces\n\n*Faux Pas* has a variant of every major functional interface from the Java core:\n\n - [`ThrowingRunnable`](src/main/java/org/zalando/fauxpas/ThrowingRunnable.java)\n - [`ThrowingSupplier`](src/main/java/org/zalando/fauxpas/ThrowingSupplier.java)\n - [`ThrowingConsumer`](src/main/java/org/zalando/fauxpas/ThrowingConsumer.java)\n - [`ThrowingFunction`](src/main/java/org/zalando/fauxpas/ThrowingFunction.java)\n - [`ThrowingUnaryOperator`](src/main/java/org/zalando/fauxpas/ThrowingUnaryOperator.java)\n - [`ThrowingPredicate`](src/main/java/org/zalando/fauxpas/ThrowingPredicate.java)\n - [`ThrowingBiConsumer`](src/main/java/org/zalando/fauxpas/ThrowingBiConsumer.java)\n - [`ThrowingBiFunction`](src/main/java/org/zalando/fauxpas/ThrowingBiFunction.java)\n - [`ThrowingBinaryOperator`](src/main/java/org/zalando/fauxpas/ThrowingBinaryOperator.java)\n - [`ThrowingBiPredicate`](src/main/java/org/zalando/fauxpas/ThrowingBiPredicate.java)\n\nThe followings statements apply to each of them:\n- extends the official interface, i.e. they are 100% compatible\n- [*sneakily throws*](https://projectlombok.org/features/SneakyThrows.html) the original exception\n\n#### Creation\n\nThe way the Java runtime implemented functional interfaces always requires additional type information, either by\nusing a cast or a local variable:\n\n```java\n// compiler error\nclient::read.apply(name);\n\n// too verbose\n((ThrowingFunction\u003cString, User, IOException\u003e) client::read).apply(name);\n\n// local variable may not always be desired\nThrowingFunction\u003cString, User, IOException\u003e readUser = client::read;\nreadUser.apply(name);\n```\n\nAs a workaround there is a static *factory* method for every interface type in`FauxPas`. All of them are called\n`throwingRunnable`, `throwingSupplier` and so forth. It allows for concise one-line statements:\n\n```java\nList\u003cUser\u003e users = names.stream()\n    .map(throwingFunction(client::read))\n    .collect(toList());\n```\n\n### Try-with-resources alternative\n\nTraditional `try-with-resources` statements are compiled into byte code that includes\n[unreachable parts](http://stackoverflow.com/a/17356707) and unfortunately JaCoCo has no\n[support for filtering](https://github.com/jacoco/jacoco/wiki/FilteringOptions) yet. That's why we came up with an\nalternative implementation. The [official example](https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html)\nfor the `try-with-resources` statement looks like this:\n\n```java\ntry (BufferedReader br =\n               new BufferedReader(new FileReader(path))) {\n    return br.readLine();\n}\n```\n\nCompared to ours:\n\n```java\nreturn tryWith(new BufferedReader(new FileReader(path)), br -\u003e \n    br.readLine()\n);\n```\n\n### CompletableFuture.exceptionally(Function)\n\n[`CompletableFuture.exceptionally(..)`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.function.Function-)\nis 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)\ninto a `CompletableFuture`:\n\n```java\nfuture.exceptionally(e -\u003e {\n    Throwable t = e instanceof CompletionException ? e.getCause() : e;\n\n    if (t instanceof NoRouteToHostException) {\n        return fallbackValueFor(e);\n    }\n\n    throw e instanceof CompletionException ? e : new CompletionException(t);\n})\n```\n\nUnfortunately it has a contract that makes it harder to use than it needs to:\n\n- It takes a [`Throwable`](https://docs.oracle.com/javase/8/docs/api/java/lang/Throwable.html) as an argument, but\n  doesn't allow to re-throw it *as-is*. This can be circumvented by optionally [wrapping it in a\n  `CompletionException`](http://cs.oswego.edu/pipermail/concurrency-interest/2014-August/012910.html) before\n  rethrowing it.\n- The throwable argument is [sometimes wrapped](https://stackoverflow.com/questions/27430255/surprising-behavior-of-java-8-completablefuture-exceptionally-method) \n  inside a [`CompletionException`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletionException.html)\n  and sometimes it's not, depending on whether there is any other computation step before the `exceptionally(..)` call\n  or not.\n  \nIn order to use the operation correctly one needs to follow these rules:\n1. Unwrap given throwable if it's an instance of `CompletionException`.\n2. Wrap checked exceptions in a `CompletionException` before throwing.\n\n`FauxPas.partially(..)` relives some of the pain by changing the interface and contract a bit to make it more usable.\nThe following example is functionally equivalent to the one from above:\n\n```java\nfuture.exceptionally(partially(e -\u003e {\n    if (e instanceof NoRouteToHostException) {\n        return fallbackValueFor(e);\n    }\n\n    throw e;\n}))\n```\n\n1. Takes a `ThrowingFunction\u003cThrowable, T, Throwable\u003e`, i.e. it allows clients to\n    - directly re-throw the throwable argument\n    - throw any exception during exception handling *as-is*\n2. Will automatically unwrap a `CompletionException` before passing it to the given function.\n   I.e. the supplied function will never have to deal with `CompletionException` directly. Except for the rare occasion\n   that the `CompletionException` has no cause, in which case it will be passed to the given function. \n3. Will automatically wrap any thrown `Exception` inside a `CompletionException`, if needed.\n\nThe last example is actually so common, that there is an overloaded version of `partially` that caters for this use \nparticular case:\n\n```java\n\nfuture.exceptionally(partially(NoRouteToHostException.class, this::fallbackValueFor))\n```\n\n### CompletableFuture.whenComplete(BiConsumer)\n\n```java\nfuture.whenComplete(failedWith(TimeoutException.class, e -\u003e {\n    request.cancel();\n}))\n```\n\nOther missing pieces in `CompletableFuture`'s API are `exceptionallyCompose` and `handleCompose`. Both can be seen as\na combination of `exceptionally` + `compose` and `handle` + `compose` respectively. They basically allow to supply\nanother `CompletableFuture` rather than concrete values directly. This is allows for asynchronous fallbacks:\n\n```java\nexceptionallyCompose(users.find(name), e -\u003e archive.find(name))\n```\n\n## Getting Help\n\nIf you have questions, concerns, bug reports, etc., please file an issue in this repository's [Issue Tracker](../../issues).\n\n## Getting Involved/Contributing\n\nTo contribute, simply make a pull request and add a brief description (1-2 sentences) of your addition or change. For\nmore details, check the [contribution guidelines](.github/CONTRIBUTING.md).\n\n## Alternatives\n\n- [Lombok's `@SneakyThrows`](https://projectlombok.org/features/SneakyThrows.html)\n- [Durian's Errors](https://github.com/diffplug/durian)\n- [Spotify's Completable Futures](https://github.com/spotify/completable-futures)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzalando%2Ffaux-pas","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzalando%2Ffaux-pas","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzalando%2Ffaux-pas/lists"}