All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fj.data.Either Maven / Gradle / Ivy

Go to download

Functional Java is an open source library that supports closures for the Java programming language

There is a newer version: 5.0
Show newest version
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);
    }

}