fj.data.Either Maven / Gradle / Ivy
package fj.data;
import static fj.Bottom.error;
import fj.*;
import static fj.Function.identity;
import static fj.P.p;
import fj.function.Effect1;
import static fj.Unit.unit;
import static fj.data.Array.mkArray;
import static fj.data.List.list;
import static fj.data.List.single;
import static fj.data.List.cons_;
import static fj.data.Option.some;
import java.util.Collection;
import java.util.Iterator;
/**
* The Either
type represents a value of one of two possible types (a disjoint union).
* The data constructors; Left
and Right
represent the two possible
* values. The Either
type is often used as an alternative to
* scala.Option
where Left
represents failure (by convention) and
* Right
is akin to Some
.
*
* @version %build.number%
*/
public abstract class Either {
private Either() {
}
/**
* Projects this either as a left.
*
* @return A left projection of this either.
*/
public final LeftProjection left() {
return new LeftProjection(this);
}
/**
* Projects this either as a right.
*
* @return A right projection of this either.
*/
public final RightProjection right() {
return new RightProjection(this);
}
/**
* Returns true
if this either is a left, false
otherwise.
*
* @return true
if this either is a left, false
otherwise.
*/
public abstract boolean isLeft();
/**
* Returns true
if this either is a right, false
otherwise.
*
* @return true
if this either is a right, false
otherwise.
*/
public abstract boolean isRight();
/**
* The catamorphism for either. Folds over this either breaking into left or right.
*
* @param left The function to call if this is left.
* @param right The function to call if this is right.
* @return The reduced value.
*/
public final X either(final F left, final F right) {
return isLeft() ?
left.f(left().value()) :
right.f(right().value());
}
/**
* Map the given functions across the appropriate side.
*
* @param left The function to map if this is left.
* @param right The function to map if this is right.
* @return A new either value after mapping with the appropriate function applied.
*/
public final Either bimap(final F left, final F right) {
return isLeft() ?
left(left.f(left().value())) :
right(right.f(right().value()));
}
@Override
public boolean equals(Object other) {
return Equal.equals0(Either.class, this, other, () -> Equal.eitherEqual(Equal.anyEqual(), Equal.anyEqual()));
}
@Override
public int hashCode() {
return Hash.eitherHash(Hash.anyHash(), Hash.anyHash()).hash(this);
}
/**
* If this is a left, then return the left value in right, or vice versa.
*
* @return The value of this either swapped to the opposing side.
*/
public final Either swap() {
return isLeft() ? new Right(((Left) this).a) : new Left(((Right) this).b);
}
private static final class Left extends Either {
private final A a;
Left(final A a) {
this.a = a;
}
public boolean isLeft() {
return true;
}
public boolean isRight() {
return false;
}
}
private static final class Right extends Either {
private final B b;
Right(final B b) {
this.b = b;
}
public boolean isLeft() {
return false;
}
public boolean isRight() {
return true;
}
}
/**
* A left projection of an either value.
*/
public final class LeftProjection implements Iterable {
private final Either e;
private LeftProjection(final Either e) {
this.e = e;
}
/**
* Returns an iterator for this projection. This method exists to permit the use in a for
-each loop.
*
* @return A iterator for this projection.
*/
public Iterator iterator() {
return toCollection().iterator();
}
/**
* The either value underlying this projection.
*
* @return The either value underlying this projection.
*/
public Either either() {
return e;
}
/**
* Returns the value of this projection or fails with the given error message.
*
* @param err The error message to fail with.
* @return The value of this projection
*/
public A valueE(final F0 err) {
if (e.isLeft())
//noinspection CastToConcreteClass
return ((Left) e).a;
else
throw error(err.f());
}
/**
* Returns the value of this projection or fails with the given error message.
*
* @param err The error message to fail with.
* @return The value of this projection
*/
public A valueE(final String err) {
return valueE(p(err));
}
/**
* The value of this projection or fails with a specialised error message.
*
* @return The value of this projection.
*/
public A value() {
return valueE(p("left.value on Right"));
}
/**
* The value of this projection or the given argument.
*
* @param a The value to return if this projection has no value.
* @return The value of this projection or the given argument.
*/
public A orValue(final F0 a) {
return isLeft() ? value() : a.f();
}
/**
* The value of this projection or the given argument.
*
* @param a The value to return if this projection has no value.
* @return The value of this projection or the given argument.
*/
public A orValue(final A a) {
return isLeft() ? value() : a;
}
/**
* The value of this projection or the result of the given function on the opposing projection's
* value.
*
* @param f The function to execute if this projection has no value.
* @return The value of this projection or the result of the given function on the opposing projection's
* value.
*/
public A on(final F f) {
return isLeft() ? value() : f.f(e.right().value());
}
/**
* Execute a side-effect on this projection's value if it has one.
*
* @param f The side-effect to execute.
* @return The unit value.
*/
public Unit foreach(final F f) {
if (isLeft())
f.f(value());
return unit();
}
/**
* Execute a side-effect on this projection's value if it has one.
*
* @param f The side-effect to execute.
*/
public void foreachDoEffect(final Effect1 f) {
if (isLeft())
f.f(value());
}
/**
* Map the given function across this projection's value if it has one.
*
* @param f The function to map across this projection.
* @return A new either value after mapping.
*/
public Either map(final F f) {
return isLeft() ? new Left(f.f(value())) : new Right(e.right().value());
}
/**
* Binds the given function across this projection's value if it has one.
*
* @param f The function to bind across this projection.
* @return A new either value after binding.
*/
public Either bind(final F> f) {
return isLeft() ? f.f(value()) : new Right(e.right().value());
}
/**
* Anonymous bind through this projection.
*
* @param e The value to bind with.
* @return An either after binding through this projection.
*/
public Either sequence(final Either e) {
return bind(Function.>constant(e));
}
/**
* Traverse with function that produces List (non-determinism).
*
* @param f the function to traverse with
* @return An either after traversing through this projection.
*/
public List> traverseList(final F> f) {
return isLeft() ?
f.f(value()).map(x -> Either.left(x)) :
list(Either.right(e.right().value()));
}
/**
* Anonymous bind through this projection.
*
* @param f the function to traverse with
* @return An either after traversing through this projection.
*/
public IO> traverseIO(final F> f) {
return isRight() ?
IOFunctions.map(f.f(value()), x -> Either.left(x)) :
IOFunctions.unit(Either.right(e.right().value()));
}
/**
* Returns None
if this projection has no value or if the given predicate
* p
does not hold for the value, otherwise, returns a right in Some
.
*
* @param f The predicate function to test on this projection's value.
* @return None
if this projection has no value or if the given predicate
* p
does not hold for the value, otherwise, returns a right in Some
.
*/
public Option> filter(final F f) {
return isLeft() ?
f.f(value()) ?
Option.>some(new Left<>(value())) :
Option.>none() :
Option.>none();
}
/**
* Function application on this projection's value.
*
* @param e The either of the function to apply on this projection's value.
* @return The result of function application within either.
*/
public Either apply(final Either, B> e) {
return e.left().bind(f -> map(f));
}
/**
* Returns true
if no value or returns the result of the application of the given
* function to the value.
*
* @param f The predicate function to test on this projection's value.
* @return true
if no value or returns the result of the application of the given
* function to the value.
*/
public boolean forall(final F f) {
return isRight() || f.f(value());
}
/**
* Returns false
if no value or returns the result of the application of the given
* function to the value.
*
* @param f The predicate function to test on this projection's value.
* @return false
if no value or returns the result of the application of the given
* function to the value.
*/
public boolean exists(final F f) {
return isLeft() && f.f(value());
}
/**
* Returns a single element list if this projection has a value, otherwise an empty list.
*
* @return A single element list if this projection has a value, otherwise an empty list.
*/
public List toList() {
return isLeft() ? single(value()) : List.nil();
}
/**
* Returns this projection's value in Some
if it exists, otherwise
* None
.
*
* @return This projection's value in Some
if it exists, otherwise
* None
.
*/
public Option toOption() {
return isLeft() ? some(value()) : Option.none();
}
/**
* Returns a single element array if this projection has a value, otherwise an empty array.
*
* @return A single element array if this projection has a value, otherwise an empty array.
*/
public Array toArray() {
if (isLeft()) {
final Object[] a = new Object[1];
a[0] = value();
return mkArray(a);
} else
return mkArray(new Object[0]);
}
/**
* Returns a single element stream if this projection has a value, otherwise an empty stream.
*
* @return A single element stream if this projection has a value, otherwise an empty stream.
*/
public Stream toStream() {
return isLeft() ? Stream.single(value()) : Stream.nil();
}
/**
* Projects an immutable collection of this projection.
*
* @return An immutable collection of this projection.
*/
public Collection toCollection() {
return toList().toCollection();
}
public Option> traverseOption(F> f) {
return isLeft() ?
f.f(value()).map(x -> Either.left(x)) :
Option.some(Either.right(e.right().value()));
}
public Stream> traverseStream(F> f) {
return isLeft() ?
f.f(value()).map(c -> Either.left(c)) :
Stream.single(Either.right(e.right().value()));
}
}
/**
* A right projection of an either value.
*/
public final class RightProjection implements Iterable {
private final Either e;
private RightProjection(final Either e) {
this.e = e;
}
/**
* Returns an iterator for this projection. This method exists to permit the use in a for
-each loop.
*
* @return A iterator for this projection.
*/
public Iterator iterator() {
return toCollection().iterator();
}
/**
* The either value underlying this projection.
*
* @return The either value underlying this projection.
*/
public Either either() {
return e;
}
/**
* Returns the value of this projection or fails with the given error message.
*
* @param err The error message to fail with.
* @return The value of this projection
*/
public B valueE(final F0 err) {
if (e.isRight())
//noinspection CastToConcreteClass
return ((Right) e).b;
else
throw error(err.f());
}
/**
* The value of this projection or fails with a specialised error message.
*
* @return The value of this projection.
*/
public B value() {
return valueE(p("right.value on Left"));
}
/**
* The value of this projection or the given argument.
*
* @param b The value to return if this projection has no value.
* @return The value of this projection or the given argument.
*/
public B orValue(final F0 b) {
return isRight() ? value() : b.f();
}
/**
* The value of this projection or the result of the given function on the opposing projection's
* value.
*
* @param f The function to execute if this projection has no value.
* @return The value of this projection or the result of the given function on the opposing projection's
* value.
*/
public B on(final F f) {
return isRight() ? value() : f.f(e.left().value());
}
/**
* Execute a side-effect on this projection's value if it has one.
*
* @param f The side-effect to execute.
* @return The unit value.
*/
public Unit foreach(final F f) {
if (isRight())
f.f(value());
return unit();
}
/**
* Execute a side-effect on this projection's value if it has one.
*
* @param f The side-effect to execute.
*/
public void foreachDoEffect(final Effect1 f) {
if (isRight())
f.f(value());
}
/**
* Map the given function across this projection's value if it has one.
*
* @param f The function to map across this projection.
* @return A new either value after mapping.
*/
public Either map(final F f) {
return isRight() ? new Right<>(f.f(value())) : new Left<>(e.left().value());
}
/**
* Binds the given function across this projection's value if it has one.
*
* @param f The function to bind across this projection.
* @return A new either value after binding.
*/
public Either bind(final F> f) {
return isRight() ? f.f(value()) : new Left<>(e.left().value());
}
/**
* Anonymous bind through this projection.
*
* @param e The value to bind with.
* @return An either after binding through this projection.
*/
public Either sequence(final Either e) {
return bind(Function.>constant(e));
}
/**
* Traverse with function that produces List (non-determinism).
*
* @param f the function to traverse with
* @return An either after traversing through this projection.
*/
public List> traverseList(final F> f) {
return isRight() ?
f.f(value()).map(x -> Either.right(x)) :
list(Either.left(e.left().value()));
}
/**
* Traverse with a function that has IO effect
*
* @param f the function to traverse with
* @return An either after traversing through this projection.
*/
public IO> traverseIO(final F> f) {
return isRight() ?
IOFunctions.map(f.f(value()), x -> Either.right(x)) :
IOFunctions.lazy(() -> Either.left(e.left().value()));
}
public P1> traverseP1(final F> f) {
return isRight() ?
f.f(value()).map(x -> Either.right(x)) :
P.p(Either.left(e.left().value()));
}
public Option> traverseOption(final F> f) {
return isRight() ?
f.f(value()).map(x -> Either.right(x)) :
Option.some(Either.left(e.left().value()));
}
/**
* Returns None
if this projection has no value or if the given predicate
* p
does not hold for the value, otherwise, returns a left in Some
.
*
* @param f The predicate function to test on this projection's value.
* @return None
if this projection has no value or if the given predicate
* p
does not hold for the value, otherwise, returns a left in Some
.
*/
public Option> filter(final F f) {
return isRight() ?
f.f(value()) ?
Option.>some(new Right(value())) :
Option.>none() :
Option.>none();
}
/**
* Function application on this projection's value.
*
* @param e The either of the function to apply on this projection's value.
* @return The result of function application within either.
*/
public Either apply(final Either> e) {
return e.right().bind(f -> map(f));
}
/**
* Returns true
if no value or returns the result of the application of the given
* function to the value.
*
* @param f The predicate function to test on this projection's value.
* @return true
if no value or returns the result of the application of the given
* function to the value.
*/
public boolean forall(final F f) {
return isLeft() || f.f(value());
}
/**
* Returns false
if no value or returns the result of the application of the given
* function to the value.
*
* @param f The predicate function to test on this projection's value.
* @return false
if no value or returns the result of the application of the given
* function to the value.
*/
public boolean exists(final F f) {
return isRight() && f.f(value());
}
/**
* Returns a single element list if this projection has a value, otherwise an empty list.
*
* @return A single element list if this projection has a value, otherwise an empty list.
*/
public List toList() {
return isRight() ? single(value()) : List.nil();
}
/**
* Returns this projection's value in Some
if it exists, otherwise
* None
.
*
* @return This projection's value in Some
if it exists, otherwise
* None
.
*/
public Option toOption() {
return isRight() ? some(value()) : Option.none();
}
/**
* Returns a single element array if this projection has a value, otherwise an empty array.
*
* @return A single element array if this projection has a value, otherwise an empty array.
*/
public Array toArray() {
if (isRight()) {
final Object[] a = new Object[1];
a[0] = value();
return mkArray(a);
} else
return Array.empty();
}
/**
* Returns a single element stream if this projection has a value, otherwise an empty stream.
*
* @return A single element stream if this projection has a value, otherwise an empty stream.
*/
public Stream toStream() {
return isRight() ? Stream.single(value()) : Stream.nil();
}
/**
* Projects an immutable collection of this projection.
*
* @return An immutable collection of this projection.
*/
public Collection toCollection() {
return toList().toCollection();
}
public Stream> traverseStream(F> f) {
return isRight() ?
f.f(value()).map(x -> Either.right(x)) :
Stream.>single(Either.left(e.left().value()));
}
}
/**
* Construct a left value of either.
*
* @param a The value underlying the either.
* @return A left value of either.
*/
public static Either left(final A a) {
return new Left(a);
}
/**
* A function that constructs a left value of either.
*
* @return A function that constructs a left value of either.
*/
public static F> left_() {
return a -> left(a);
}
/**
* A function that constructs a right value of either.
*
* @return A function that constructs a right value of either.
*/
public static F> right_() {
return b -> right(b);
}
/**
* Construct a right value of either.
*
* @param b The value underlying the either.
* @return A right value of either.
*/
public static Either right(final B b) {
return new Right(b);
}
/**
* @return A function that maps another function across an either's left projection.
*/
public static F, F, Either>> leftMap_() {
return axf -> e -> e.left().map(axf);
}
/**
* @return A function that maps another function across an either's right projection.
*/
public static F, F, Either>> rightMap_() {
return axf -> e -> e.right().map(axf);
}
/**
* Joins an either through left.
*
* @param e The either of either to join.
* @return An either after joining.
*/
public static Either joinLeft(final Either, B> e) {
final F, Either> id = identity();
return e.left().bind(id);
}
/**
* Joins an either through right.
*
* @param e The either of either to join.
* @return An either after joining.
*/
public static Either joinRight(final Either> e) {
final F, Either> id = identity();
return e.right().bind(id);
}
/**
* Sequences through the left side of the either monad with a list of values.
*
* @param a The list of values to sequence with the either monad.
* @return A sequenced value.
*/
public static Either, X> sequenceLeft(final List> a) {
return a.isEmpty() ?
Either., X>left(List.nil()) :
a.head().left().bind(aa -> sequenceLeft(a.tail()).left().map(cons_(aa)));
}
/**
* Sequences through the right side of the either monad with a list of values.
*
* @param a The list of values to sequence with the either monad.
* @return A sequenced value.
*/
public static Either> sequenceRight(final List> a) {
return a.isEmpty() ?
Either.>right(List.nil()) :
a.head().right().bind(bb -> sequenceRight(a.tail()).right().map(cons_(bb)));
}
/**
* Traversable instance of RightProjection of Either for List.
*
* @return traversed value
*/
public List> traverseListRight(final F> f) {
return right().traverseList(f);
}
/**
* Traversable instance of LeftProjection of Either for List.
*
* @return traversed value
*/
public List> traverseListLeft(final F> f) {
return left().traverseList(f);
}
/**
* Traversable instance of RightProjection of Either for IO.
*
* @return traversed value
*/
public IO> traverseIORight(final F> f) {
return right().traverseIO(f);
}
/**
* Traversable instance of LeftProjection of Either for IO.
*
* @return traversed value
*/
public IO> traverseIOLeft(final F> f) {
return left().traverseIO(f);
}
/**
* Traversable instance of RightProjection of Either for Option.
*
* @return traversed value
*/
public Option> traverseOptionRight(final F> f) {
return right().traverseOption(f);
}
/**
* Traversable instance of LeftProjection of Either for Option.
*
* @return traversed value
*/
public Option> traverseOptionLeft(final F> f) {
return left().traverseOption(f);
}
/**
* Traversable instance of RightProjection of Either for Stream.
*
* @return traversed value
*/
public Stream> traverseStreamRight(final F> f) {
return right().traverseStream(f);
}
/**
* Traversable instance of LeftProjection of Either for Stream.
*
* @return traversed value
*/
public Stream> traverseStreamLeft(final F> f) {
return left().traverseStream(f);
}
/**
* Takes an Either
to its contained value within left or right.
*
* @param e The either to reduce.
* @return An Either
to its contained value within left or right.
*/
public static A reduce(final Either e) {
return e.isLeft() ? e.left().value() : e.right().value();
}
/**
* If the condition satisfies, return the given A in left, otherwise, return the given B in right.
*
* @param c The condition to test.
* @param right The right value to use if the condition satisfies.
* @param left The left value to use if the condition does not satisfy.
* @return A constructed either based on the given condition.
*/
public static Either iif(final boolean c, final F0 right, final F0 left) {
return c ? new Right(right.f()) : new Left(left.f());
}
/**
* Returns all the left values in the given list.
*
* @param es The list of possible left values.
* @return All the left values in the given list.
*/
public static List lefts(final List> es) {
return es.foldRight(e -> as -> e.isLeft() ? as.cons(e.left().value()) : as, List.nil());
}
/**
* Returns all the right values in the given list.
*
* @param es The list of possible right values.
* @return All the right values in the given list.
*/
public static List rights(final List> es) {
return es.foldRight(e -> bs -> e.isRight() ? bs.cons(e.right().value()) : bs, List.nil());
}
public String toString() {
return Show.eitherShow(Show.anyShow(), Show.anyShow()).showS(this);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy