
fj.data.Either Maven / Gradle / Ivy
package fj.data;
import fj.Equal;
import fj.F;
import fj.F0;
import fj.Function;
import fj.Hash;
import fj.P1;
import fj.Show;
import fj.Unit;
import fj.function.Effect1;
import java.util.Collection;
import java.util.Iterator;
import static fj.Bottom.error;
import static fj.Function.compose;
import static fj.Function.identity;
import static fj.P.p;
import static fj.Unit.unit;
import static fj.data.Array.mkArray;
import static fj.data.List.cons_;
import static fj.data.List.list;
import static fj.data.List.single;
import static fj.data.Option.some;
/**
* 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 abstract X either(final F left, final F right);
/**
* 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 either(compose(left_(), left), compose(right_(), right));
}
@Override
public final boolean equals(Object other) {
return Equal.equals0(Either.class, this, other, () -> Equal.eitherEqual(Equal.anyEqual(), Equal.anyEqual()));
}
@Override
public final 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 either(right_(), left_());
}
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;
}
@Override
public X either(F left, F right) {
return left.f(a);
}
}
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;
}
@Override
public X either(F left, F right) {
return right.f(b);
}
}
/**
* A left projection of an either value.
*/
public static 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 e.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 e.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 e.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 (e.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 (e.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 e.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 e.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 e.isLeft() ?
f.f(value()).map(Either::left) :
list(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 e.isLeft() ?
IOFunctions.map(f.f(value()), Either::left) :
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 e.isLeft() ?
f.f(value()) ?
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(this::map);
}
/**
* 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 e.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 e.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 e.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 e.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 (e.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 e.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 e.isLeft() ?
f.f(value()).map(left_()) :
some(right(e.right().value()));
}
public Stream> traverseStream(F> f) {
return e.isLeft() ?
f.f(value()).map(left_()) :
Stream.single(right(e.right().value()));
}
}
/**
* A right projection of an either value.
*/
public static 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 e.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 e.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 (e.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 (e.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 e.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 e.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 e.isRight() ?
f.f(value()).map(right_()) :
list(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 e.isRight() ?
IOFunctions.map(f.f(value()), right_()) :
IOFunctions.lazy(() -> left(e.left().value()));
}
public P1> traverseP1(final F> f) {
return e.isRight() ?
f.f(value()).map(right_()) :
p(left(e.left().value()));
}
public Option> traverseOption(final F> f) {
return e.isRight() ?
f.f(value()).map(right_()) :
some(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 e.isRight() ?
f.f(value()) ?
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(this::map);
}
/**
* 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 e.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 e.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 e.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 e.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 (e.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 e.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 e.isRight() ?
f.f(value()).map(Either::right) :
Stream.single(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 Either::left;
}
/**
* A function that constructs a right value of either.
*
* @return A function that constructs a right value of either.
*/
public static F> right_() {
return Either::right;
}
/**
* 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);
}
/**
* First class 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 reducing function.
*/
public static F, X> either_(final F left, final F right) {
return e -> e.either(left, right);
}
/**
* @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) {
return e.left().bind(identity());
}
/**
* 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) {
return e.right().bind(identity());
}
/**
* 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() ?
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() ?
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 final List> traverseListRight(final F> f) {
return right().traverseList(f);
}
/**
* Traversable instance of LeftProjection of Either for List.
*
* @return traversed value
*/
public final List> traverseListLeft(final F> f) {
return left().traverseList(f);
}
/**
* Traversable instance of RightProjection of Either for IO.
*
* @return traversed value
*/
public final IO> traverseIORight(final F> f) {
return right().traverseIO(f);
}
/**
* Traversable instance of LeftProjection of Either for IO.
*
* @return traversed value
*/
public final IO> traverseIOLeft(final F> f) {
return left().traverseIO(f);
}
/**
* Traversable instance of RightProjection of Either for Option.
*
* @return traversed value
*/
public final Option> traverseOptionRight(final F> f) {
return right().traverseOption(f);
}
/**
* Traversable instance of LeftProjection of Either for Option.
*
* @return traversed value
*/
public final Option> traverseOptionLeft(final F> f) {
return left().traverseOption(f);
}
/**
* Traversable instance of RightProjection of Either for Stream.
*
* @return traversed value
*/
public final Stream> traverseStreamRight(final F> f) {
return right().traverseStream(f);
}
/**
* Traversable instance of LeftProjection of Either for Stream.
*
* @return traversed value
*/
public final 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.either(identity(), identity());
}
/**
* 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 final String toString() {
return Show.eitherShow(Show.anyShow(), Show.anyShow()).showS(this);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy