{"id":24469536,"url":"https://github.com/mverleg/java-result","last_synced_at":"2025-04-13T10:24:57.525Z","repository":{"id":42463054,"uuid":"337205126","full_name":"mverleg/java-result","owner":"mverleg","description":"Algebraic Result type in Java 15+, return type for oprations that can fail","archived":false,"fork":false,"pushed_at":"2022-04-04T21:07:12.000Z","size":306,"stargazers_count":6,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-27T01:47:53.866Z","etag":null,"topics":["algebraic-data-types","error-handling","java","jvm","result","sealed-class","types"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mverleg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-02-08T20:46:55.000Z","updated_at":"2024-06-06T13:21:17.000Z","dependencies_parsed_at":"2022-08-30T11:30:19.514Z","dependency_job_id":null,"html_url":"https://github.com/mverleg/java-result","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mverleg%2Fjava-result","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mverleg%2Fjava-result/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mverleg%2Fjava-result/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mverleg%2Fjava-result/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mverleg","download_url":"https://codeload.github.com/mverleg/java-result/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248696855,"owners_count":21147203,"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":["algebraic-data-types","error-handling","java","jvm","result","sealed-class","types"],"created_at":"2025-01-21T07:15:54.218Z","updated_at":"2025-04-13T10:24:57.485Z","avatar_url":"https://github.com/mverleg.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n[![javadoc](https://javadoc.io/badge2/nl.markv/result/javadoc.svg)](https://javadoc.io/doc/nl.markv/result/latest/nl/markv/result/Result.html)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/nl.markv/result/badge.svg)](https://search.maven.org/artifact/nl.markv/result)\n[![Unit tests](https://github.com/mverleg/java-result/actions/workflows/test.yml/badge.svg)](https://github.com/mverleg/java-result/actions/workflows/test.yml)\n[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](./LICENSE.txt)\n\n# Java `Result` type\n\n`Result` is a value that can be either `Ok` or `Err`, to signal whether an operation succeeded or failed. Each variant can contain data, e.g. `Result\u003cUser, ErrMsg\u003e` contains a `User` if ok, and `ErrMsg` when it fails.\n\nIt can be used as a return value from functions to indicate if they succeeded or failed, similar to `Optional`, but with data about _why_ it failed.\n\n## Project status\n\nJava-result is feature-complete and can be used in Java 15+. It has extensive unit test coverage, but limited real-world testing.\n\n## Examples\n\nFunctions can return `Result` to indicate whether they failed and why:\n\n```java\n@Nonnull\npublic static Result\u003cInteger, DivError\u003e divide(@Nullable Integer numerator, @Nullable Integer divisor) {\n    if (null == numerator) {\n        return Result.err(DivError.NUMERATOR_NULL);\n    }\n    if (null == divisor) {\n        return Result.err(DivError.DIVISOR_NULL);\n    }\n    if (0 == divisor) {\n        return Result.err(DivError.DIVISOR_ZERO);\n    }\n    return Result.ok(numerator / divisor);\n}\n```\n\n* Only use the value **if successful**\n  \n   ```java\n   Result\u003cInteger, DivError\u003e costPerPerson = divide(cost, people);\n   if (costPerPerson.isOk()) {\n       sendMessage(\"You need to pay \" + costPerPerson.get());\n   }\n   ```\n\n   or more tersely:\n\n   ```java\n   divide(cost, people)\n        .ifOk(costPerPerson -\u003e sendMessage(\"You need to pay \" + costPerPerson));\n   ```\n\n* **Chain operations**, transforming the results only if it is successful:\n\n   ```java\n   divide(9, 0).map(result -\u003e result / 2)\n   // Err(DIVISOR_ZERO)\n   ```\n   \n   of if the transformation can also fail:\n\n   ```java\n   divide(8, 2).flatMap(res -\u003e divide(res, 2))\n   // Ok(2)\n   ```\n\n* **Fallback** to a default in case of failure:\n\n   ```java\n   divide(8, 0).withoutErr().orElse(1)\n   // Ok(1)\n   divide(8, 0).withoutErr().orElseGet(() -\u003e calculateFallback())\n   // calculateFallback() computed only if failed\n   divide(8, 0).withoutErr().recover(err -\u003e calculateFallback2(err))\n   // calculateFallback2(err) computed only if failed\n   ```\n\n* **From exception** to `Result`:\n\n   ```java\n   Result\u003cString, Exception\u003e userName = Result.attempt(() -\u003e findUserName());\n   ```\n\n* If you have decided to **not handle errors**:\n\n   ```java\n   divide(8, divisor).getOrThrow(\"if you see this, sorry...\")\n   // throws\n   ```\n  \n* Handle success result or **adjust type**:\n\n   ```java\n   Result\u003cString, Exception\u003e userNameResult = Result.attempt(() -\u003e findUserName());\n   if (userNameResult instanceof Ok\u003cString, Exception\u003e userName) {\n       return doSomethingWithUsername(userName.get());\n   } else {\n       return userNameResult.adaptOk();\n       // Changes the ok type, which is safe because this is a failed Tesult\n   }\n   ```\n\n* **Keep only success** results, because `Result.stream` contains only the success value or nothing:\n\n   ```java\n   List\u003cResult\u003cInteger, String\u003e\u003e list = List.of(ok(1), ok(2), err(\"problem\"), ok(4));\n   List\u003cInteger\u003e successesOnly = list.stream()\n       .flatMap(Result::stream).toList();\n   // [1, 2, 4]\n   ```\n\n* Get all success values if all results are all successful, or the first error otherwise:\n\n   ```java\n   List\u003cResult\u003cInteger, String\u003e\u003e list = List.of(ok(1), ok(2), err(\"problem\"), ok(4));\n   Result\u003cList\u003cInteger\u003e, String\u003e listResult = Result.transpose(list);\n   // Err(problem)\n   ```\n  \n   There is also a `Stream` collector that does the same thing:\n\n   ```java\n   Result\u003cList\u003cInteger\u003e, DivError\u003e streamResult = Stream.of(2, 1, 0, -1, -2)\n       .map(nr -\u003e divide(10, nr))\n       .collect(ResultCollector.toList());\n   // Err(DIVISOR_ZERO)\n   ```\n\nThere is a lot more, [have a look at the source](src/main/java/nl/markv/result/Result.java).\n\n## Install\n\nJava-result is available on Central: [nl.markv.result](https://search.maven.org/artifact/nl.markv/result).\n\n### Maven\n\nAdd this dependency:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003enl.markv\u003c/groupId\u003e\n    \u003cartifactId\u003eresult\u003c/artifactId\u003e\n    \u003cversion\u003e1.1.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nFor Java 15/16 this uses preview features. Java 14 and below are not supported.\n\n### Gradle\n\nFor Java 15+, add this dependency:\n\n```groovy\nimplementation 'nl.markv:result:1.1.0'\n```\n\nFor Java 15/16 this uses preview features. Java 14 and below are not supported.\n\n## Sealed types in Java\n\nWith sealed interfaces in Java 15 (preview feature), it finally has decent support for sum types - algebraic types that can have one out of a finite set of values. They are sometimes called unions or composite types.\n\nYou can think of it as an enhanced [`enum`](https://docs.oracle.com/en/java/javase/13/language/switch-expressions.html), where each variant is a different subtype, instead of single instance. Each variant can have a different structure, and can have any number of instances.\n\nMany languages that support sum types, like [Kotlin](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-result/), [Haskell](https://hackage.haskell.org/package/base-4.14.1.0/docs/Data-Either.html), [Rust](https://doc.rust-lang.org/std/result/), [Swift](https://www.swiftbysundell.com/articles/the-power-of-result-types-in-swift/), [C++](https://bell0bytes.eu/expected/) or [others](https://en.wikipedia.org/wiki/Result_type), have some kind of type that indicates one of two options - for example, success or failure.  Java has [`Optional`](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html), but that cannot contain an error value.\n\nResult is a popular example of such types, which has two variants: one for success and one for failure. It can be used for error handling.\n\nIf you are familiar with [monads](https://adambennett.dev/2020/05/the-result-monad/), `Result` is a monad with unit operations `ok`/`err`, bind operation `map`/`mapErr`, and a flattening operation `flatMap`/`flatMapErr` or `flatten`. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmverleg%2Fjava-result","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmverleg%2Fjava-result","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmverleg%2Fjava-result/lists"}