Skip to content

Instantly share code, notes, and snippets.

@electroCutie
Last active October 13, 2022 11:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save electroCutie/79696fa1bad5d9eb35d2f464de728d86 to your computer and use it in GitHub Desktop.
Save electroCutie/79696fa1bad5d9eb35d2f464de728d86 to your computer and use it in GitHub Desktop.
package cloud.literallya.util;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
* Either a left or a right. This contains exactly one non-null value of either the Left or Right type
*/
public final class Either<L, R> {
private final L left;
private final R right;
private Either(L l, R r){
checkArgument(null == l ^ null == r);
this.left = l;
this.right = r;
}
public boolean isRight(){
return null == this.left;
}
public boolean isLeft(){
return null == this.right;
}
/**
* Takes a function for the left and right and applies the appropriate one to the contained value
*/
public <L2, R2> Either<L2, R2> map(Function<L, L2> l, Function<R, R2> r){
requireNonNull(l);
requireNonNull(r);
if(null == left)
return new Either<>(null, requireNonNull(r.apply(right)));
else
return new Either<>(requireNonNull(l.apply(left)), null);
}
/**
* Maps the left value if this object contains a left
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public <L2> Either<L2, R> mapLeft(Function<L, L2> l){
requireNonNull(l);
if(null == left)
return (Either) this;
return new Either<>(requireNonNull(l.apply(left)), null);
}
/**
* Maps the right value if this object contains a right
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public <R2> Either<L, R2> mapRight(Function<R, R2> r){
requireNonNull(r);
if(null == right)
return (Either) this;
return new Either<>(null, requireNonNull(r.apply(right)));
}
/**
* Takes two functions, one from left and one from right, that both return the same kind of Either and returns that kind of
* Either
*
* @param l
* Function from Left to Either<L2, R2>
* @param r
* Function from Right to Either<L2, R2>
*/
public <L2, R2> Either<L2, R2> flatMap(Function<L, Either<L2, R2>> l, Function<R, Either<L2, R2>> r){
requireNonNull(l);
requireNonNull(r);
if(null == left)
return r.apply(right);
else
return l.apply(left);
}
/**
* Take a function from left to an Either with a new kind of left, and returns that kind of Either, only applying the function
* if this either is a left
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public <L2> Either<L2, R> flatMapLeft(Function<L, Either<L2, R>> l){
requireNonNull(l);
if(null == left)
return (Either) this;
return l.apply(left);
}
/**
* Take a function from right to an Either with a new kind of right, and returns that kind of Either, only applying the
* function if this either is a right
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public <R2> Either<L, R2> flatMapRight(Function<R, Either<L, R2>> r){
requireNonNull(r);
if(null == right)
return (Either) this;
return r.apply(right);
}
/**
* Given a consumer for a left and a right this will execute one of them with the contained value
*/
public void ifEither(Consumer<L> l, Consumer<R> r){
requireNonNull(l);
requireNonNull(r);
if(null == left)
r.accept(right);
else
l.accept(left);
}
/**
* Consume this Either if it is a left
*/
public void ifLeft(Consumer<L> l){
if(null != left)
l.accept(left);
}
/**
* Consume this Either if it is a right
*/
public void ifRight(Consumer<R> r){
if(null != right)
r.accept(right);
}
/**
* Takes a pair of functions, one from left and the other from right, both to the same type and returns the result of one of
* those applications
*/
public <M> M merge(Function<L, M> l, Function<R, M> r){
requireNonNull(l);
requireNonNull(r);
if(null == left)
return r.apply(right);
else
return l.apply(left);
}
/**
* Unwrap this either into an Optional of the left hand type
*/
public Optional<L> getLeft(){
return Optional.ofNullable(left);
}
public Stream<L> streamLeft(){
return getLeft().stream();
}
/**
* Unwrap this either into an Optional of the right hand type
*/
public Optional<R> getRight(){
return Optional.ofNullable(right);
}
public Stream<R> streamRight(){
return getRight().stream();
}
/*
* Consructors
*/
/**
* Construct and Either containing a Left. Must not be null
*/
public static <L, R> Either<L, R> ofLeft(L l){
return new Either<>(requireNonNull(l), null);
}
/**
* Construct and Either containing a Right. Must not be null
*/
public static <L, R> Either<L, R> ofRight(R r){
return new Either<>(null, requireNonNull(r));
}
/**
* An Either of left if not null, otherwise right. One of the two must not be null
*/
public static <L, R> Either<L, R> ofLeftOrElseRight(L l, R r){
if(null != l)
return new Either<L, R>(l, null);
else if(null != r)
return new Either<L, R>(null, r);
throw new IllegalArgumentException("Left and rigt both null");
}
/**
* An Either of right if not null, otherwise left. One of the two must not be null.
* Note that the "right" comes first
*/
public static <L, R> Either<L, R> ofRightOrElseLeft(R r, L l){
if(null != r)
return new Either<L, R>(null, r);
else if(null != l)
return new Either<L, R>(l, null);
throw new IllegalArgumentException("Left and rigt both null");
}
/**
* Left if not null, or else invoke the supplier to get the right
*/
public static <L, R> Either<L, R> ofLeftOrGetRight(L l, Supplier<R> r){
requireNonNull(r);
if(null != l)
return new Either<L, R>(l, null);
else
return new Either<L, R>(null, requireNonNull(r.get()));
}
/**
* Right if not null, or else invoke the supplier to get the Left
*/
public static <L, R> Either<L, R> ofRightOrGetLeft(R r, Supplier<L> l){
requireNonNull(l);
if(null != r)
return new Either<L, R>(null, r);
else
return new Either<L, R>(requireNonNull(l.get()), null);
}
/*
* Object stuff
*/
@Override
public boolean equals(Object obj){
Either e;
return (obj instanceof Either) && (((e = (Either) obj) == this) ||
(Objects.equals(this.right, e.right) && Objects.equals(this.left, e.left)));
}
@Override
public int hashCode(){
final int xor = this.isLeft() ? 0x5c5c5c5c : ~0x5c5c5c5c;
final int hash = Objects.hashCode(this.isLeft() ? this.left.hashCode() : this.right.hashCode());
return xor ^ hash;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment