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

fj.data.optic.POptional 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.optic;

import fj.F;
import fj.Function;
import fj.Monoid;
import fj.P;
import fj.P1;
import fj.P2;
import fj.Semigroup;
import fj.control.Trampoline;
import fj.control.parallel.Promise;
import fj.control.parallel.Strategy;
import fj.data.Either;
import fj.data.IO;
import fj.data.IOFunctions;
import fj.data.List;
import fj.data.Option;
import fj.data.Stream;
import fj.data.Validation;
import fj.data.vector.V2;

/**
 * A {@link POptional} can be seen as a pair of functions: - `getOrModify: S => T \/ A` - `set : (B, S) => T`
 *
 * A {@link POptional} could also be defined as a weaker {@link PLens} and weaker {@link PPrism}
 *
 * {@link POptional} stands for Polymorphic Optional as it set and modify methods change a type `A` to `B` and `S` to `T`.
 * {@link Optional} is a {@link POptional} restricted to monomoprhic updates: {{{ type Optional[S, A] = POptional[S, S, A, A]
 * }}}
 *
 * @param  the source of a {@link POptional}
 * @param  the modified source of a {@link POptional}
 * @param  the target of a {@link POptional}
 * @param  the modified target of a {@link POptional}
 */
public abstract class POptional {

  POptional() {
    super();
  }

  /** get the target of a {@link POptional} or modify the source in case there is no target */
  public abstract Either getOrModify(S s);

  /** get the modified source of a {@link POptional} */
  public abstract F set(final B b);

  /** get the target of a {@link POptional} or nothing if there is no target */
  public abstract Option getOption(final S s);

  /**
   * modify polymorphically the target of a {@link POptional} with an Applicative function
   */
  public abstract  F> modifyFunctionF(final F> f);

  /**
   * modify polymorphically the target of a {@link POptional} with an Applicative function
   */
  public abstract  F> modifyEitherF(final F> f);

  /**
   * modify polymorphically the target of a {@link POptional} with an Applicative function
   */
  public abstract F> modifyIOF(F> f);

  /**
   * modify polymorphically the target of a {@link POptional} with an Applicative function
   */
  public abstract F> modifyTrampolineF(F> f);

  /**
   * modify polymorphically the target of a {@link POptional} with an Applicative function
   */
  public abstract F> modifyPromiseF(F> f);

  /**
   * modify polymorphically the target of a {@link POptional} with an Applicative function
   */
  public abstract F> modifyListF(F> f);

  /**
   * modify polymorphically the target of a {@link POptional} with an Applicative function
   */
  public abstract F> modifyOptionF(F> f);

  /**
   * modify polymorphically the target of a {@link POptional} with an Applicative function
   */
  public abstract F> modifyStreamF(F> f);

  /**
   * modify polymorphically the target of a {@link POptional} with an Applicative function
   */
  public abstract F> modifyP1F(F> f);

  /**
   * modify polymorphically the target of a {@link POptional} with an Applicative function
   */
  public abstract  F> modifyValidationF(F> f);

  /**
   * modify polymorphically the target of a {@link POptional} with an Applicative function
   */
  public abstract F> modifyV2F(F> f);

  /** modify polymorphically the target of a {@link POptional} with a function */
  public abstract F modify(final F f);

  /**
   * modify polymorphically the target of a {@link POptional} with a function. return empty if the {@link POptional} is not
   * matching
   */
  public final F> modifyOption(final F f) {
    return s -> getOption(s).map(Function.constant(modify(f).f(s)));
  }

  /** set polymorphically the target of a {@link POptional} with a value. return empty if the {@link POptional} is not matching */
  public final F> setOption(final B b) {
    return modifyOption(Function.constant(b));
  }

  /** check if a {@link POptional} has a target */
  public final boolean isMatching(final S s) {
    return getOption(s).isSome();

  }

  /** join two {@link POptional} with the same target */
  public final  POptional, Either, A, B> sum(final POptional other) {
    return pOptional(
        e -> e.either(s -> getOrModify(s).left().map(Either.left_()), s1 -> other.getOrModify(s1).left().map(Either.right_())),
        b -> e -> e.bimap(set(b), other.set(b)));
  }

  public  POptional, P2, P2, P2> first() {
    return pOptional(
        sc -> getOrModify(sc._1()).bimap(t -> P.p(t, sc._2()), a -> P.p(a, sc._2())),
        bc -> s_ -> P.p(set(bc._1()).f(s_._1()), bc._2()));
  }

  public  POptional, P2, P2, P2> second() {
    return pOptional(
        cs -> getOrModify(cs._2()).bimap(t -> P.p(cs._1(), t), a -> P.p(cs._1(), a)),
        cb -> _s -> P.p(cb._1(), set(cb._2()).f(_s._2())));
  }

  /***************************************************************/
  /** Compose methods between a {@link POptional} and another Optics */
  /***************************************************************/

  /** compose a {@link POptional} with a {@link Fold} */
  public final  Fold composeFold(final Fold other) {
    return asFold().composeFold(other);
  }

  /** compose a {@link POptional} with a {@link Getter} */
  public final  Fold composeGetter(final Getter other) {
    return asFold().composeGetter(other);
  }

  /** compose a {@link POptional} with a {@link PSetter} */
  public final  PSetter composeSetter(final PSetter other) {
    return asSetter().composeSetter(other);
  }

  /** compose a {@link POptional} with a {@link PTraversal} */
  public final  PTraversal composeTraversal(final PTraversal other) {
    return asTraversal().composeTraversal(other);
  }

  /** compose a {@link POptional} with a {@link POptional} */
  public final  POptional composeOptional(final POptional other) {
    final POptional self = this;
    return new POptional() {

      @Override
      public Either getOrModify(final S s) {
        return self.getOrModify(s).right()
            .bind(a -> other.getOrModify(a).bimap(b -> POptional.this.set(b).f(s), Function.identity()));
      }

      @Override
      public F set(final D d) {
        return self.modify(other.set(d));
      }

      @Override
      public Option getOption(final S s) {
        return self.getOption(s).bind(other::getOption);
      }

      @Override
      public  F> modifyFunctionF(final F> f) {
        return self.modifyFunctionF(other.modifyFunctionF(f));
      }

      @Override
      public  F> modifyEitherF(final F> f) {
        return self.modifyEitherF(other.modifyEitherF(f));
      }

      @Override
      public F> modifyIOF(final F> f) {
        return self.modifyIOF(other.modifyIOF(f));
      }

      @Override
      public F> modifyTrampolineF(final F> f) {
        return self.modifyTrampolineF(other.modifyTrampolineF(f));
      }

      @Override
      public F> modifyPromiseF(final F> f) {
        return self.modifyPromiseF(other.modifyPromiseF(f));
      }

      @Override
      public F> modifyListF(final F> f) {
        return self.modifyListF(other.modifyListF(f));
      }

      @Override
      public F> modifyOptionF(final F> f) {
        return self.modifyOptionF(other.modifyOptionF(f));
      }

      @Override
      public F> modifyStreamF(final F> f) {
        return self.modifyStreamF(other.modifyStreamF(f));
      }

      @Override
      public F> modifyP1F(final F> f) {
        return self.modifyP1F(other.modifyP1F(f));
      }

      @Override
      public  F> modifyValidationF(final F> f) {
        return self.modifyValidationF(other.modifyValidationF(f));
      }

      @Override
      public F> modifyV2F(final F> f) {
        return self.modifyV2F(other.modifyV2F(f));
      }

      @Override
      public F modify(final F f) {
        return self.modify(other.modify(f));
      }
    };
  }

  /** compose a {@link POptional} with a {@link PPrism} */
  public final  POptional composePrism(final PPrism other) {
    return composeOptional(other.asOptional());
  }

  /** compose a {@link POptional} with a {@link PLens} */
  public final  POptional composeLens(final PLens other) {
    return composeOptional(other.asOptional());
  }

  /** compose a {@link POptional} with a {@link PIso} */
  public final  POptional composeIso(final PIso other) {
    return composeOptional(other.asOptional());
  }

  /*********************************************************************/
  /** Transformation methods to view a {@link POptional} as another Optics */
  /*********************************************************************/

  /** view a {@link POptional} as a {@link Fold} */
  public final Fold asFold() {
    return new Fold() {
      @Override
      public  F foldMap(final Monoid m, final F f) {
        return s -> POptional.this.getOption(s).map(f).orSome(m.zero());
      }
    };
  }

  /** view a {@link POptional} as a {@link PSetter} */
  public PSetter asSetter() {
    return new PSetter() {
      @Override
      public F modify(final F f) {
        return POptional.this.modify(f);
      }

      @Override
      public F set(final B b) {
        return POptional.this.set(b);
      }
    };
  }

  /** view a {@link POptional} as a {@link PTraversal} */
  public PTraversal asTraversal() {
    final POptional self = this;
    return new PTraversal() {

      @Override
      public  F> modifyFunctionF(final F> f) {
        return self.modifyFunctionF(f);
      }

      @Override
      public  F> modifyEitherF(final F> f) {
        return self.modifyEitherF(f);
      }

      @Override
      public F> modifyIOF(final F> f) {
        return self.modifyIOF(f);
      }

      @Override
      public F> modifyTrampolineF(final F> f) {
        return self.modifyTrampolineF(f);
      }

      @Override
      public F> modifyPromiseF(final F> f) {
        return self.modifyPromiseF(f);
      }

      @Override
      public F> modifyListF(final F> f) {
        return self.modifyListF(f);
      }

      @Override
      public F> modifyOptionF(final F> f) {
        return self.modifyOptionF(f);
      }

      @Override
      public F> modifyStreamF(final F> f) {
        return self.modifyStreamF(f);
      }

      @Override
      public F> modifyP1F(final F> f) {
        return self.modifyP1F(f);
      }

      @Override
      public  F> modifyValidationF(Semigroup s, final F> f) {
        return self.modifyValidationF(f);
      }

      @Override
      public F> modifyV2F(final F> f) {
        return self.modifyV2F(f);
      }

      @Override
      public  F foldMap(final Monoid monoid, final F f) {
        return s -> self.getOption(s).map(f).orSome(monoid.zero());
      }
    };
  }

  public static  POptional pId() {
    return PIso. pId().asOptional();
  }

  /** create a {@link POptional} using the canonical functions: getOrModify and set */
  public static  POptional pOptional(final F> getOrModify, final F> set) {
    return new POptional() {
      @Override
      public Either getOrModify(final S s) {
        return getOrModify.f(s);
      }

      @Override
      public F set(final B b) {
        return set.f(b);
      }

      @Override
      public Option getOption(final S s) {
        return getOrModify.f(s).right().toOption();
      }

      @Override
      public  F> modifyFunctionF(final F> f) {
        return s -> getOrModify.f(s).either(
            Function.constant(),
            a -> Function.compose(b -> set.f(b).f(s), f.f(a))
            );
      }

      @Override
      public  F> modifyEitherF(final F> f) {
        return s -> getOrModify.f(s).either(
            Either.right_(),
            t -> f.f(t).right().map(b -> set.f(b).f(s))
            );
      }

      @Override
      public F> modifyIOF(final F> f) {
        return s -> getOrModify.f(s).either(
            IOFunctions::unit,
            t -> IOFunctions.map(f.f(t), b -> set.f(b).f(s))
            );
      }

      @Override
      public F> modifyTrampolineF(final F> f) {
        return s -> getOrModify.f(s).either(
            Trampoline.pure(),
            t -> f.f(t).map(b -> set.f(b).f(s))
            );
      }

      @Override
      public F> modifyPromiseF(final F> f) {
        return s -> getOrModify.f(s).either(
            t -> Promise.promise(Strategy.idStrategy(), P.p(t)),
            t -> f.f(t).fmap(b -> set.f(b).f(s))
            );
      }

      @Override
      public F> modifyListF(final F> f) {
        return s -> getOrModify.f(s).either(
            List::single,
            t -> f.f(t).map(b -> set.f(b).f(s))
            );
      }

      @Override
      public F> modifyOptionF(final F> f) {
        return s -> getOrModify.f(s).either(
            Option.some_(),
            t -> f.f(t).map(b -> set.f(b).f(s))
            );
      }

      @Override
      public F> modifyStreamF(final F> f) {
        return s -> getOrModify.f(s).either(
            Stream.single(),
            t -> f.f(t).map(b -> set.f(b).f(s))
            );
      }

      @Override
      public F> modifyP1F(final F> f) {
        return s -> getOrModify.f(s).either(
            P.p1(),
            t -> f.f(t).map(b -> set.f(b).f(s))
            );
      }

      @Override
      public  F> modifyValidationF(final F> f) {
        return s -> getOrModify.f(s).either(
            Validation::success,
            t -> f.f(t).map(b -> set.f(b).f(s))
            );
      }

      @Override
      public F> modifyV2F(final F> f) {
        return s -> getOrModify.f(s).either(
            t -> V2.p(P.p(t, t)),
            t -> f.f(t).map(b -> set.f(b).f(s))
            );
      }

      @Override
      public F modify(final F f) {
        return s -> getOrModify.f(s).either(Function.identity(), a -> set.f(f.f(a)).f(s));
      }
    };
  }

}