-
-
Notifications
You must be signed in to change notification settings - Fork 660
Closed
Description
Context:
In Java 8, the language added few functional programming Types like Optional<T> or CompletableFuture<T> (Promise) but not others like Either<L, R> which is included in this library.
Other modern programming language like Kotlin, Rust, Swift, Ocaml or F# add Result which was designed handle Error handling.
- Kotlin: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-result/
- Rust: https://doc.rust-lang.org/std/result/
- Swift: https://developer.apple.com/documentation/swift/result
- Ocaml: https://ocaml.org/manual/5.2/api/Result.html
- F#: https://fsharp.github.io/fsharp-core-docs/reference/fsharp-core-fsharpresult-2.html
Goal:
Add Result<T> in the library.
Examples:
//Error handling easily
Function<String, Result<URI>> toURI = address -> {
return Result.runCatching(() -> {
return new URI(address);
});
};
class ResultExampleTest {
@Test
void should_work() {
List<String> endpoints = Arrays.asList(
"https://jsonplaceholder.typicode.com/posts/1",
"https://jsonplaceholder.typicode.com/posts/2",
"https://jsonplaceholder.typicode.com/posts/3"
);
// @formatter:off
List<Result<String>> results = endpoints.stream()
.map(ResultExampleTest::fetchData)
.toList();
List<String> successfulResults = results.stream()
.filter(Result::isSuccess)
.map(Result::getValue)
.flatMap(Optional::stream)
.toList();
// @formatter:on
successfulResults.forEach(System.out::println);
assertThat(successfulResults.size()).isGreaterThan(0);
}
private static Result<String> fetchData(String endpoint) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(endpoint)).build();
return Result.runCatching(() -> {
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
return response.body();
} else {
throw new IOException("Failed to fetch data from " + endpoint);
}
});
}
}
//Example using Railway-oriented programming
class ResultROPTest {
Result<Integer> divide(int dividend, int divisor) {
try {
if (divisor == 0) {
throw new IllegalArgumentException("Division by zero");
}
return Result.success(dividend / divisor);
} catch (Exception e) {
return Result.failure(e);
}
}
Result<Integer> parseInteger(String input) {
try {
int parsedValue = Integer.parseInt(input);
return Result.success(parsedValue);
} catch (NumberFormatException e) {
return Result.failure(e);
}
}
// @formatter:off
Result<Integer> calculate(String input1, String input2) {
return parseInteger(input1)
.flatMap(value1 -> parseInteger(input2)
.flatMap(value2 -> divide(value1, value2)));
}
// @formatter:on
@Test
void should_work_ok() {
Result<Integer> result = calculate("10", "2");
int finalValue = result.getOrElse(() -> 0);
assertThat(finalValue).isEqualTo(5);
}
@Test
void should_work_ko() {
Result<Integer> result = calculate("0", "2");
int finalValue = result.getOrElse(() -> 0);
assertThat(finalValue).isZero();
}
}Pending discussions:
- What design is better?
Result<T>orResult<T, E> - Add recommendations about when use Result and when use Either, Optional to minimize the usage of Java Exceptions
- Implement the solution
Note: Result<T, E> it is quite similar to Either<L, R>
Discussion branch:
Other links:
- https://arrow-kt.io/learn/typed-errors/wrappers/
- https://www.baeldung.com/kotlin/result-class
- https://github.com/jabrena/typed-errors
Juan Antonio
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels