Created
March 26, 2024 12:09
-
-
Save nithinivi/5ea7baba6f2273fc76cb605ee673d732 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package sg.dlt.indus.lib.functional; | |
import java.util.NoSuchElementException; | |
import java.util.Objects; | |
import java.util.Optional; | |
import java.util.function.Consumer; | |
import java.util.function.Function; | |
/** | |
* Either Monad represents a value of two possible types. An Either is either a {@link Left} or a | |
* {@link Right}.By convention the success case is Right and the failure is Left. | |
* | |
* @param <L> The type of the Left value of an Either. | |
* @param <R> The type of the Right value of an Either. | |
*/ | |
public interface Either<L, R> { | |
/** | |
* Matches the value of this Either instance against two functions. | |
* | |
* @param left The function to apply if the value is of type A (left). | |
* @param right The function to apply if the value is of type B (right). | |
* @param <C> The return type of the functions. | |
* @return The result of applying the appropriate function to the value | |
* depends on instance initialization . | |
*/ | |
<C> C match(Function<? super L, ? extends C> left, | |
Function<? super R, ? extends C> right); | |
/** | |
* Returns whether this Either is a Left. | |
* | |
* <pre>{@code | |
* // prints "true" | |
* System.out.println(Either.left("error").isLeft()); | |
* | |
* // prints "false" | |
* System.out.println(Either.right(42).isLeft()); | |
* }</pre> | |
* | |
* @return true, if this is a Left, false otherwise | |
*/ | |
boolean isLeft(); | |
/** | |
* Returns whether this Either is a Right. | |
* | |
* <pre>{@code | |
* // prints "true" | |
* System.out.println(Either.right(42).isRight()); | |
* | |
* // prints "false" | |
* System.out.println(Either.left("error").isRight()); | |
* }</pre> | |
* | |
* @return true, if this is a Right, false otherwise | |
*/ | |
boolean isRight(); | |
/** | |
* Gets the {@code Left} value or throws. | |
* | |
* @return the left value, if the underlying {@code Either} is a {@code Left} | |
* @throws NoSuchElementException if the underlying {@code Either} of this {@code LeftProjection} is a {@code Right} | |
*/ | |
L getLeft(); | |
/** | |
* Gets the {@code Right} value or throws. | |
* | |
* @return the right value, if the underlying {@code Either} is a {@code Right} | |
* @throws NoSuchElementException if the underlying {@code Either} of this {@code RightProjection} is a {@code Left} | |
*/ | |
R get(); | |
/** | |
* Constructs a {@link Left} | |
* | |
* <pre>{@code | |
* // Creates Either instance initiated with right value 1 | |
* Either<Integer, ? > either = Either.left(1); | |
* }</pre> | |
* | |
* @param left The value. | |
* @param <L> Type of left value. | |
* @param <R> Type of right value. | |
* @return A new {@code Left} instance. | |
*/ | |
static <L, R> Either<L, R> left(L left) { | |
return new Left<>(left); | |
} | |
/** | |
* Constructs a {@link Right} | |
* | |
* <pre>{@code | |
* // Creates Either instance initiated with right value 1 | |
* Either<?, Integer> either = Either.right(1); | |
* }</pre> | |
* | |
* @param right The value. | |
* @param <L> Type of left value. | |
* @param <R> Type of right value. | |
* @return A new {@code Right} instance. | |
*/ | |
static <L, R> Either<L, R> right(R right) { | |
return new Right<>(right); | |
} | |
/** | |
* Extracts the value of type A if the Either instance holds a value of type A (left). | |
* | |
* @return An Optional containing the value of type A if it exists, otherwise an empty Optional. | |
*/ | |
default Optional<L> fromLeft() { | |
return this.match(Optional::of, value -> Optional.empty()); | |
} | |
/** | |
* Extracts the value of type B if the Either instance holds a value of type B (right). | |
* | |
* @return An Optional containing the value of type B if it exists, otherwise an empty Optional. | |
*/ | |
default Optional<R> fromRight() { | |
return this.match(value -> Optional.empty(), Optional::of); | |
} | |
/** | |
* Consumes {@link Right} and {@link Left} | |
* | |
* <pre>{@code | |
* List<Integer> intList = new ArrayList<>(); | |
* List<String> stringList = new ArrayList<>(); | |
* | |
* Either<String, Integer> either | |
* either.consumer( | |
* intList::add, | |
* stringList::add | |
* ) | |
* }</pre> | |
* | |
* @throws NullPointerException if {@code left} or {@code right} is null | |
*/ | |
default void consume(Consumer<? super L> left, | |
Consumer<? super R> right) { | |
Objects.requireNonNull(left, "left is null"); | |
Objects.requireNonNull(right, "right is null"); | |
if (isLeft()) | |
left.accept(getLeft()); | |
else | |
right.accept(get()); | |
} | |
/** | |
* If a value is {@link Right}, performs the given action with the value, | |
* otherwise does nothing. | |
* | |
* @param action the action to be performed, if a {@link Right} is present | |
* @throws NullPointerException if action is | |
* {@code null} | |
*/ | |
default void ifRight(Consumer<R> action) { | |
Objects.requireNonNull(action, "action is null"); | |
if (isRight()) { | |
action.accept(get()); | |
} | |
} | |
} | |
class Right<L, R> implements Either<L, R> { | |
private final R value; | |
public Right(R value) { | |
this.value = value; | |
} | |
public <C> C match(Function<? super L, ? extends C> left, | |
Function<? super R, ? extends C> right) { | |
return right.apply(value); | |
} | |
@Override | |
public boolean isLeft() { | |
return false; | |
} | |
@Override | |
public boolean isRight() { | |
return true; | |
} | |
@Override | |
public L getLeft() { | |
throw new NoSuchElementException("getLeft() on Right"); | |
} | |
@Override | |
public R get() { | |
return value; | |
} | |
} | |
class Left<L, R> implements Either<L, R> { | |
private final L value; | |
public Left(L value) { | |
this.value = value; | |
} | |
public <C> C match(Function<? super L, ? extends C> left, | |
Function<? super R, ? extends C> right) { | |
return left.apply(value); | |
} | |
@Override | |
public boolean isLeft() { | |
return true; | |
} | |
@Override | |
public boolean isRight() { | |
return false; | |
} | |
@Override | |
public L getLeft() { | |
return value; | |
} | |
@Override | |
public R get() { | |
throw new NoSuchElementException("get() on Left"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment