From a367d0f2e4cdbb070dea2e07d38ab5ae4c2b4ab3 Mon Sep 17 00:00:00 2001 From: Jose Luis Leon Date: Mon, 6 Nov 2023 01:50:18 -0500 Subject: [PATCH] chore(either): Simplify Either implementation (#217) --- .../io/github/joselion/maybe/util/Either.java | 166 ++++++------------ .../io/github/joselion/maybe/util/Either.java | 166 ++++++------------ 2 files changed, 112 insertions(+), 220 deletions(-) diff --git a/src/main/java/io/github/joselion/maybe/util/Either.java b/src/main/java/io/github/joselion/maybe/util/Either.java index ee1dfab..65b9c08 100644 --- a/src/main/java/io/github/joselion/maybe/util/Either.java +++ b/src/main/java/io/github/joselion/maybe/util/Either.java @@ -49,19 +49,37 @@ static Either ofRight(final R value) { return new Right<>(value); } + /** + * Terminal operator. Unwraps the {@code Either} to obtain the wrapped value. + * Since there's no possible way for the compiler to know which one is + * present ({@code Left} or {@code Right}), you need to provide a handler for + * both cases. Only the handler with the value present is used to unwrap and + * return the value. + * + * @param the type of the returned value + * @param onLeft a function to handle the left value if present + * @param onRight a function to handle the right value if present + * @return either the left or the right handled value + */ + T unwrap(Function onLeft, Function onRight); + /** * Returns true if the {@code Left} value is present, false otherwise. * * @return true if left is present, false otherwise */ - boolean isLeft(); + default boolean isLeft() { + return unwrap(left -> true, right -> false); + } /** * Returns true if the {@code Right} value is present, false otherwise. * * @return true if right is present, false otherwise */ - boolean isRight(); + default boolean isRight() { + return unwrap(left -> false, rigth -> true); + } /** * Run an effect if the {@code Left} value is present. Does nothing otherwise. @@ -69,7 +87,15 @@ static Either ofRight(final R value) { * @param effect a consumer function that receives the left value * @return the same {@code Either} instance */ - Either doOnLeft(Consumer effect); + default Either doOnLeft(final Consumer effect) { + return unwrap( + left -> { + effect.accept(left); + return Either.ofLeft(left); + }, + Either::ofRight + ); + } /** * Run an effect if the {@code Right} value is present. Does nothing otherwise. @@ -77,7 +103,15 @@ static Either ofRight(final R value) { * @param effect effect a consumer function that receives the right value * @return the same {@code Either} instance */ - Either doOnRight(Consumer effect); + default Either doOnRight(final Consumer effect) { + return unwrap( + Either::ofLeft, + right -> { + effect.accept(right); + return Either.ofRight(right); + } + ); + } /** * Map the {@code Left} value to another if present. Does nothing otherwise. @@ -86,7 +120,12 @@ static Either ofRight(final R value) { * @param mapper a function that receives the left value and returns another * @return an {@code Either} instance with the mapped left value */ - Either mapLeft(Function mapper); + default Either mapLeft(final Function mapper) { + return unwrap( + left -> Either.ofLeft(mapper.apply(left)), + Either::ofRight + ); + } /** * Map the {@code Right} value to another if present. Does nothing otherwise. @@ -95,7 +134,12 @@ static Either ofRight(final R value) { * @param mapper a function that receives the right value and returns another * @return an {@code Either} instance with the mapped right value */ - Either mapRight(Function mapper); + default Either mapRight(final Function mapper) { + return unwrap( + Either::ofLeft, + right -> Either.ofRight(mapper.apply(right)) + ); + } /** * Terminal operator. Returns the {@code Left} value if present. Otherwise, @@ -104,7 +148,9 @@ static Either ofRight(final R value) { * @param fallback the value to return if left is not present * @return the left value or a fallback */ - L leftOrElse(L fallback); + default L leftOrElse(final L fallback) { + return unwrap(Function.identity(), rigth -> fallback); + } /** * Terminal operator. Returns the {@code Right} value if present. Otherwise, @@ -113,21 +159,9 @@ static Either ofRight(final R value) { * @param fallback the value to return if right is not present * @return the right value or a fallback */ - R rightOrElse(R fallback); - - /** - * Terminal operator. Unwraps the {@code Either} to obtain the wrapped value. - * Since there's no possible way for the compiler to know which one is - * present ({@code Left} or {@code Right}), you need to provide a handler for - * both cases. Only the handler with the value present is used to unwrap and - * return the value. - * - * @param the type of the returned value - * @param onLeft a function to handle the left value if present - * @param onRight a function to handle the right value if present - * @return either the left or the right handled value - */ - T unwrap(Function onLeft, Function onRight); + default R rightOrElse(final R fallback) { + return unwrap(left -> fallback, Function.identity()); + } /** * Terminal operator. Returns the {@code Left} value if present. Otherwise, @@ -193,50 +227,6 @@ L value() { return this.value; } - @Override - public boolean isLeft() { - return true; - } - - @Override - public boolean isRight() { - return false; - } - - @Override - public Either doOnLeft(final Consumer effect) { - effect.accept(this.value); - - return this; - } - - @Override - public Either doOnRight(final Consumer effect) { - return this; - } - - @Override - public Either mapLeft(final Function mapper) { - final var mappedLeft = mapper.apply(this.value); - - return new Left<>(mappedLeft); - } - - @Override - public Either mapRight(final Function mapper) { - return new Left<>(this.value); - } - - @Override - public L leftOrElse(final L fallback) { - return this.value; - } - - @Override - public R rightOrElse(final R fallback) { - return fallback; - } - @Override public T unwrap(final Function onLeft, final Function onRight) { return onLeft.apply(this.value); @@ -291,50 +281,6 @@ R value() { return this.value; } - @Override - public boolean isLeft() { - return false; - } - - @Override - public boolean isRight() { - return true; - } - - @Override - public Either doOnLeft(final Consumer effect) { - return this; - } - - @Override - public Either doOnRight(final Consumer effect) { - effect.accept(this.value); - - return this; - } - - @Override - public Either mapLeft(final Function mapper) { - return new Right<>(this.value); - } - - @Override - public Either mapRight(final Function mapper) { - final var mappedRight = mapper.apply(this.value); - - return new Right<>(mappedRight); - } - - @Override - public L leftOrElse(final L fallback) { - return fallback; - } - - @Override - public R rightOrElse(final R fallback) { - return this.value; - } - @Override public T unwrap(final Function onLeft, final Function onRight) { return onRight.apply(this.value); diff --git a/src/main/java17/io/github/joselion/maybe/util/Either.java b/src/main/java17/io/github/joselion/maybe/util/Either.java index d5ff374..226f170 100644 --- a/src/main/java17/io/github/joselion/maybe/util/Either.java +++ b/src/main/java17/io/github/joselion/maybe/util/Either.java @@ -49,19 +49,37 @@ static Either ofRight(final R value) { return new Right<>(value); } + /** + * Terminal operator. Unwraps the {@code Either} to obtain the wrapped value. + * Since there's no possible way for the compiler to know which one is + * present ({@code Left} or {@code Right}), you need to provide a handler for + * both cases. Only the handler with the value present is used to unwrap and + * return the value. + * + * @param the type of the returned value + * @param onLeft a function to handle the left value if present + * @param onRight a function to handle the right value if present + * @return either the left or the right handled value + */ + T unwrap(Function onLeft, Function onRight); + /** * Returns true if the {@code Left} value is present, false otherwise. * * @return true if left is present, false otherwise */ - boolean isLeft(); + default boolean isLeft() { + return unwrap(left -> true, right -> false); + } /** * Returns true if the {@code Right} value is present, false otherwise. * * @return true if right is present, false otherwise */ - boolean isRight(); + default boolean isRight() { + return unwrap(left -> false, rigth -> true); + } /** * Run an effect if the {@code Left} value is present. Does nothing otherwise. @@ -69,7 +87,15 @@ static Either ofRight(final R value) { * @param effect a consumer function that receives the left value * @return the same {@code Either} instance */ - Either doOnLeft(Consumer effect); + default Either doOnLeft(final Consumer effect) { + return unwrap( + left -> { + effect.accept(left); + return Either.ofLeft(left); + }, + Either::ofRight + ); + } /** * Run an effect if the {@code Right} value is present. Does nothing otherwise. @@ -77,7 +103,15 @@ static Either ofRight(final R value) { * @param effect effect a consumer function that receives the right value * @return the same {@code Either} instance */ - Either doOnRight(Consumer effect); + default Either doOnRight(final Consumer effect) { + return unwrap( + Either::ofLeft, + right -> { + effect.accept(right); + return Either.ofRight(right); + } + ); + } /** * Map the {@code Left} value to another if present. Does nothing otherwise. @@ -86,7 +120,12 @@ static Either ofRight(final R value) { * @param mapper a function that receives the left value and returns another * @return an {@code Either} instance with the mapped left value */ - Either mapLeft(Function mapper); + default Either mapLeft(final Function mapper) { + return unwrap( + left -> Either.ofLeft(mapper.apply(left)), + Either::ofRight + ); + } /** * Map the {@code Right} value to another if present. Does nothing otherwise. @@ -95,7 +134,12 @@ static Either ofRight(final R value) { * @param mapper a function that receives the right value and returns another * @return an {@code Either} instance with the mapped right value */ - Either mapRight(Function mapper); + default Either mapRight(final Function mapper) { + return unwrap( + Either::ofLeft, + right -> Either.ofRight(mapper.apply(right)) + ); + } /** * Terminal operator. Returns the {@code Left} value if present. Otherwise, @@ -104,7 +148,9 @@ static Either ofRight(final R value) { * @param fallback the value to return if left is not present * @return the left value or a fallback */ - L leftOrElse(L fallback); + default L leftOrElse(final L fallback) { + return unwrap(Function.identity(), rigth -> fallback); + } /** * Terminal operator. Returns the {@code Right} value if present. Otherwise, @@ -113,21 +159,9 @@ static Either ofRight(final R value) { * @param fallback the value to return if right is not present * @return the right value or a fallback */ - R rightOrElse(R fallback); - - /** - * Terminal operator. Unwraps the {@code Either} to obtain the wrapped value. - * Since there's no possible way for the compiler to know which one is - * present ({@code Left} or {@code Right}), you need to provide a handler for - * both cases. Only the handler with the value present is used to unwrap and - * return the value. - * - * @param the type of the returned value - * @param onLeft a function to handle the left value if present - * @param onRight a function to handle the right value if present - * @return either the left or the right handled value - */ - T unwrap(Function onLeft, Function onRight); + default R rightOrElse(final R fallback) { + return unwrap(left -> fallback, Function.identity()); + } /** * Terminal operator. Returns the {@code Left} value if present. Otherwise, @@ -187,50 +221,6 @@ record Left(L value) implements Either { Objects.requireNonNull(value, "An Either cannot be created with a null value"); } - @Override - public boolean isLeft() { - return true; - } - - @Override - public boolean isRight() { - return false; - } - - @Override - public Either doOnLeft(final Consumer effect) { - effect.accept(this.value); - - return this; - } - - @Override - public Either doOnRight(final Consumer effect) { - return this; - } - - @Override - public Either mapLeft(final Function mapper) { - final var mappedLeft = mapper.apply(this.value); - - return new Left<>(mappedLeft); - } - - @Override - public Either mapRight(final Function mapper) { - return new Left<>(this.value); - } - - @Override - public L leftOrElse(final L fallback) { - return this.value; - } - - @Override - public R rightOrElse(final R fallback) { - return fallback; - } - @Override public T unwrap(final Function onLeft, final Function onRight) { return onLeft.apply(this.value); @@ -278,50 +268,6 @@ record Right(R value) implements Either { Objects.requireNonNull(value, "An Either cannot be created with a null value"); } - @Override - public boolean isLeft() { - return false; - } - - @Override - public boolean isRight() { - return true; - } - - @Override - public Either doOnLeft(final Consumer effect) { - return this; - } - - @Override - public Either doOnRight(final Consumer effect) { - effect.accept(this.value); - - return this; - } - - @Override - public Either mapLeft(final Function mapper) { - return new Right<>(this.value); - } - - @Override - public Either mapRight(final Function mapper) { - final var mappedRight = mapper.apply(this.value); - - return new Right<>(mappedRight); - } - - @Override - public L leftOrElse(final L fallback) { - return fallback; - } - - @Override - public R rightOrElse(final R fallback) { - return this.value; - } - @Override public T unwrap(final Function onLeft, final Function onRight) { return onRight.apply(this.value);