Skip to content

Commit

Permalink
chore(either): Simplify Either implementation (#217)
Browse files Browse the repository at this point in the history
  • Loading branch information
JoseLion committed Nov 6, 2023
1 parent 11e6b8f commit a367d0f
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 220 deletions.
166 changes: 56 additions & 110 deletions src/main/java/io/github/joselion/maybe/util/Either.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,35 +49,69 @@ static <L, R> Either<L, R> 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 <T> 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> T unwrap(Function<L, T> onLeft, Function<R, T> 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.
*
* @param effect a consumer function that receives the left value
* @return the same {@code Either} instance
*/
Either<L, R> doOnLeft(Consumer<L> effect);
default Either<L, R> doOnLeft(final Consumer<L> 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.
*
* @param effect effect a consumer function that receives the right value
* @return the same {@code Either} instance
*/
Either<L, R> doOnRight(Consumer<R> effect);
default Either<L, R> doOnRight(final Consumer<R> 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.
Expand All @@ -86,7 +120,12 @@ static <L, R> Either<L, R> 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
*/
<T> Either<T, R> mapLeft(Function<L, T> mapper);
default <T> Either<T, R> mapLeft(final Function<L, T> mapper) {
return unwrap(
left -> Either.ofLeft(mapper.apply(left)),
Either::ofRight
);
}

/**
* Map the {@code Right} value to another if present. Does nothing otherwise.
Expand All @@ -95,7 +134,12 @@ static <L, R> Either<L, R> 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
*/
<T> Either<L, T> mapRight(Function<R, T> mapper);
default <T> Either<L, T> mapRight(final Function<R, T> mapper) {
return unwrap(
Either::ofLeft,
right -> Either.ofRight(mapper.apply(right))
);
}

/**
* Terminal operator. Returns the {@code Left} value if present. Otherwise,
Expand All @@ -104,7 +148,9 @@ static <L, R> Either<L, R> 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,
Expand All @@ -113,21 +159,9 @@ static <L, R> Either<L, R> 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 <T> 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> T unwrap(Function<L, T> onLeft, Function<R, T> onRight);
default R rightOrElse(final R fallback) {
return unwrap(left -> fallback, Function.identity());
}

/**
* Terminal operator. Returns the {@code Left} value if present. Otherwise,
Expand Down Expand Up @@ -193,50 +227,6 @@ L value() {
return this.value;
}

@Override
public boolean isLeft() {
return true;
}

@Override
public boolean isRight() {
return false;
}

@Override
public Either<L, R> doOnLeft(final Consumer<L> effect) {
effect.accept(this.value);

return this;
}

@Override
public Either<L, R> doOnRight(final Consumer<R> effect) {
return this;
}

@Override
public <T> Either<T, R> mapLeft(final Function<L, T> mapper) {
final var mappedLeft = mapper.apply(this.value);

return new Left<>(mappedLeft);
}

@Override
public <T> Either<L, T> mapRight(final Function<R, T> 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> T unwrap(final Function<L, T> onLeft, final Function<R, T> onRight) {
return onLeft.apply(this.value);
Expand Down Expand Up @@ -291,50 +281,6 @@ R value() {
return this.value;
}

@Override
public boolean isLeft() {
return false;
}

@Override
public boolean isRight() {
return true;
}

@Override
public Either<L, R> doOnLeft(final Consumer<L> effect) {
return this;
}

@Override
public Either<L, R> doOnRight(final Consumer<R> effect) {
effect.accept(this.value);

return this;
}

@Override
public <T> Either<T, R> mapLeft(final Function<L, T> mapper) {
return new Right<>(this.value);
}

@Override
public <T> Either<L, T> mapRight(final Function<R, T> 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> T unwrap(final Function<L, T> onLeft, final Function<R, T> onRight) {
return onRight.apply(this.value);
Expand Down
Loading

0 comments on commit a367d0f

Please sign in to comment.