fj.data.Either Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of functionaljava Show documentation
Show all versions of functionaljava Show documentation
Functional Java is an open source library that supports closures for the Java programming language
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.isRight() ?
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);
}
}