Last active
October 13, 2022 11:57
-
-
Save electroCutie/79696fa1bad5d9eb35d2f464de728d86 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 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