From b80650b6ae8ca509044580606b6910a8f76c6fbb Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Fri, 20 Jul 2018 18:40:33 +0100 Subject: [PATCH] Add `Result.reduce(Result,BinaryOperator)` --- src/main/java/net/kemitix/mon/result/Err.java | 6 ++ .../java/net/kemitix/mon/result/Result.java | 80 +++++++++++-------- .../java/net/kemitix/mon/result/Success.java | 16 ++-- src/test/java/net/kemitix/mon/ResultTest.java | 60 ++++++++++++++ 4 files changed, 123 insertions(+), 39 deletions(-) diff --git a/src/main/java/net/kemitix/mon/result/Err.java b/src/main/java/net/kemitix/mon/result/Err.java index 65fcb96..61a46f1 100644 --- a/src/main/java/net/kemitix/mon/result/Err.java +++ b/src/main/java/net/kemitix/mon/result/Err.java @@ -26,6 +26,7 @@ import net.kemitix.mon.maybe.Maybe; import java.util.Objects; import java.util.concurrent.Callable; +import java.util.function.BinaryOperator; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -101,6 +102,11 @@ class Err implements Result { return this; } + @Override + public Result reduce(final Result identify, final BinaryOperator operator) { + return Result.error(error); + } + @Override public boolean equals(final Object other) { return other instanceof Err && Objects.equals(error, ((Err) other).error); diff --git a/src/main/java/net/kemitix/mon/result/Result.java b/src/main/java/net/kemitix/mon/result/Result.java index 4240f67..58d7bec 100644 --- a/src/main/java/net/kemitix/mon/result/Result.java +++ b/src/main/java/net/kemitix/mon/result/Result.java @@ -25,10 +25,7 @@ import net.kemitix.mon.Functor; import net.kemitix.mon.maybe.Maybe; import java.util.concurrent.Callable; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; +import java.util.function.*; /** * An Either type for holding a result or an error (Throwable). @@ -63,17 +60,6 @@ public interface Result extends Functor> { return new Err<>(error); } - /** - * Create a Result for a success. - * - * @param value the value - * @param the type of the value - * @return a successful Result - */ - static Result ok(final T value) { - return new Success<>(value); - } - /** * Create a Result for a output of the Callable. * @@ -90,6 +76,17 @@ public interface Result extends Functor> { } } + /** + * Create a Result for a success. + * + * @param value the value + * @param the type of the value + * @return a successful Result + */ + static Result ok(final T value) { + return new Success<>(value); + } + /** * Creates a {@link Maybe} from the Result, where the Result is a success, then the Maybe will contain the value. * @@ -108,6 +105,15 @@ public interface Result extends Functor> { } } + /** + * Extracts the successful value from the result, or throws the error Throwable. + * + * @return the value if a success + * @throws Throwable the result is an error + */ + @SuppressWarnings("illegalthrows") + T orElseThrow() throws Throwable; + /** * Swaps the inner Result of a Maybe, so that a Result is on the outside. * @@ -122,6 +128,15 @@ public interface Result extends Functor> { .flatMap(value -> Result.ok(Maybe.maybe(value))); } + /** + * Returns a new Result consisting of the result of applying the function to the contents of the Result. + * + * @param f the mapping function the produces a Result + * @param the type of the value withing the Result of the mapping function + * @return a Result + */ + Result flatMap(Function> f); + /** * Applies the function to the contents of a Maybe within the Result. * @@ -131,7 +146,10 @@ public interface Result extends Functor> { * @param the type of the updated Result * @return a new Maybe within a Result */ - static Result> flatMapMaybe(Result> maybeResult, Function, Result>> f) { + static Result> flatMapMaybe( + final Result> maybeResult, + final Function, Result>> f + ) { return maybeResult.flatMap(f); } @@ -149,15 +167,6 @@ public interface Result extends Functor> { */ boolean isOkay(); - /** - * Returns a new Result consisting of the result of applying the function to the contents of the Result. - * - * @param f the mapping function the produces a Result - * @param the type of the value withing the Result of the mapping function - * @return a Result - */ - Result flatMap(Function> f); - @Override Result map(Function f); @@ -177,15 +186,6 @@ public interface Result extends Functor> { */ Result> maybe(Predicate predicate); - /** - * Extracts the successful value from the result, or throws the error Throwable. - * - * @return the value if a success - * @throws Throwable the result is an error - */ - @SuppressWarnings("illegalthrows") - T orElseThrow() throws Throwable; - /** * Provide the value within the Result, if it is a success, to the Consumer, and returns this Result. * @@ -251,4 +251,16 @@ public interface Result extends Functor> { * @return the Result or a new error Result */ Result thenWith(Function> f); + + /** + * Reduce two Results of the same type into one using the reducing function provided. + * + *

If either Result is an error, then the reduce will return the error. If both are errors, then the error of + * {@link this} Result will be returned.

+ * + * @param identify the identify Result + * @param operator the function to combine the values the Results + * @return a Result containing the combination of the two Results + */ + Result reduce(Result identify, BinaryOperator operator); } diff --git a/src/main/java/net/kemitix/mon/result/Success.java b/src/main/java/net/kemitix/mon/result/Success.java index b7d0ef3..4c91623 100644 --- a/src/main/java/net/kemitix/mon/result/Success.java +++ b/src/main/java/net/kemitix/mon/result/Success.java @@ -26,6 +26,7 @@ import net.kemitix.mon.maybe.Maybe; import java.util.Objects; import java.util.concurrent.Callable; +import java.util.function.BinaryOperator; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -51,11 +52,6 @@ class Success implements Result { return true; } - @Override - public Result flatMap(final Function> f) { - return f.apply(value); - } - @Override public Result map(final Function f) { return Result.ok(f.apply(value)); @@ -105,6 +101,16 @@ class Success implements Result { return f.apply(value).call(this); } + @Override + public Result reduce(final Result identity, final BinaryOperator operator) { + return flatMap(a -> identity.flatMap(b -> Result.of(() -> operator.apply(a, b)))); + } + + @Override + public Result flatMap(final Function> f) { + return f.apply(value); + } + @Override public boolean equals(final Object other) { return other instanceof Success && Objects.equals(value, ((Success) other).value); diff --git a/src/test/java/net/kemitix/mon/ResultTest.java b/src/test/java/net/kemitix/mon/ResultTest.java index edda0e3..2558d69 100644 --- a/src/test/java/net/kemitix/mon/ResultTest.java +++ b/src/test/java/net/kemitix/mon/ResultTest.java @@ -671,6 +671,66 @@ public class ResultTest implements WithAssertions { ); } + @Test + public void okayOkay_whenReduce_thenCombine() { + //given + final Result result1 = Result.ok(1); + final Result result10 = Result.ok(10); + //when + final Result result11 = result1.reduce(result10, (a, b) -> a + b); + //then + result11.match( + success -> assertThat(success).isEqualTo(11), + error -> fail("Not an error") + ); + } + + @Test + public void okayError_whenReduce_thenError() { + //given + final Result result1 = Result.ok(1); + final RuntimeException exception = new RuntimeException(); + final Result result10 = Result.error(exception); + //when + final Result result11 = result1.reduce(result10, (a, b) -> a + b); + //then + result11.match( + success->fail("Not a success"), + error -> assertThat(error).isSameAs(exception) + ); + } + + @Test + public void errorOkay_whenReduce_thenError() { + //given + final RuntimeException exception = new RuntimeException(); + final Result result1 = Result.error(exception); + final Result result10 = Result.ok(10); + //when + final Result result11 = result1.reduce(result10, (a, b) -> a + b); + //then + result11.match( + success->fail("Not a success"), + error -> assertThat(error).isSameAs(exception) + ); + } + + @Test + public void errorError_whenReduce_thenError() { + //given + final RuntimeException exception1 = new RuntimeException(); + final Result result1 = Result.error(exception1); + final RuntimeException exception10 = new RuntimeException(); + final Result result10 = Result.error(exception10); + //when + final Result result11 = result1.reduce(result10, (a, b) -> a + b); + //then + result11.match( + success->fail("Not a success"), + error -> assertThat(error).isSameAs(exception1) + ); + } + @RequiredArgsConstructor private static class UseCase {