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

fj.data.optic.PPrism 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.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 PPrism} can be seen as a pair of functions: - `getOrModify: S => T \/ A` - `reverseGet : B => T`
 *
 * A {@link PPrism} could also be defined as a weaker {@link PIso} where get can fail.
 *
 * Typically a {@link PPrism} or {@link Prism} encodes the relation between a Sum or CoProduct type (e.g. sealed trait) and one
 * of it is element.
 *
 * {@link PPrism} stands for Polymorphic Prism as it set and modify methods change a type `A` to `B` and `S` to `T`.
 * {@link Prism} is a {@link PPrism} where the type of target cannot be modified.
 *
 * A {@link PPrism} is also a valid {@link Fold}, {@link POptional}, {@link PTraversal} and {@link PSetter}
 *
 * @param  the source of a {@link PPrism}
 * @param  the modified source of a {@link PPrism}
 * @param  the target of a {@link PPrism}
 * @param  the modified target of a {@link PPrism}
 */
public abstract class PPrism {

  PPrism() {
    super();
  }

  /** get the target of a {@link PPrism} or modify the source in case there is no target */

  public abstract Either getOrModify(S s);

  /** get the modified source of a {@link PPrism} */
  public abstract T reverseGet(B b);

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

  /** modify polymorphically the target of a {@link PPrism} with an Applicative function */
  public final  F> modifyFunctionF(final F> f) {
    return s -> getOrModify(s).either(
        Function.constant(),
        a -> Function.compose(this::reverseGet, f.f(a))
        );
  }

  /** modify polymorphically the target of a {@link PPrism} with an Applicative function */
  public final  F> modifyEitherF(final F> f) {
    return s -> getOrModify(s).either(
        Either.right_(),
        t -> f.f(t).right().map(this::reverseGet)
        );
  }

  /** modify polymorphically the target of a {@link PPrism} with an Applicative function */
  public final F> modifyIOF(final F> f) {
    return s -> getOrModify(s).either(
        IOFunctions::unit,
        t -> IOFunctions.map(f.f(t), this::reverseGet)
        );
  }

  /** modify polymorphically the target of a {@link PPrism} with an Applicative function */
  public final F> modifyTrampolineF(final F> f) {
    return s -> getOrModify(s).either(
        Trampoline.pure(),
        t -> f.f(t).map(this::reverseGet)
        );
  }

  /** modify polymorphically the target of a {@link PPrism} with an Applicative function */
  public final F> modifyPromiseF(final F> f) {
    return s -> getOrModify(s).either(
        t -> Promise.promise(Strategy.idStrategy(), P.p(t)),
        t -> f.f(t).fmap(this::reverseGet)
        );
  }

  /** modify polymorphically the target of a {@link PPrism} with an Applicative function */
  public final F> modifyListF(final F> f) {
    return s -> getOrModify(s).either(
        List::single,
        t -> f.f(t).map(this::reverseGet)
        );
  }

  /** modify polymorphically the target of a {@link PPrism} with an Applicative function */
  public final F> modifyOptionF(final F> f) {
    return s -> getOrModify(s).either(
        Option.some_(),
        t -> f.f(t).map(this::reverseGet)
        );
  }

  /** modify polymorphically the target of a {@link PPrism} with an Applicative function */
  public final F> modifyStreamF(final F> f) {
    return s -> getOrModify(s).either(
        Stream.single(),
        t -> f.f(t).map(this::reverseGet)
        );
  }

  /** modify polymorphically the target of a {@link PPrism} with an Applicative function */
  public final F> modifyP1F(final F> f) {
    return s -> getOrModify(s).either(
        P.p1(),
        t -> f.f(t).map(this::reverseGet)
        );
  }

  /** modify polymorphically the target of a {@link PPrism} with an Applicative function */
  public final  F> modifyValidationF(final F> f) {
    return s -> getOrModify(s).either(
        Validation::success,
        t -> f.f(t).map(this::reverseGet)
        );
  }

  /** modify polymorphically the target of a {@link PPrism} with an Applicative function */
  public final F> modifyV2F(final F> f) {
    return s -> getOrModify(s).either(
        t -> V2.p(P.p(t, t)),
        t -> f.f(t).map(this::reverseGet)
        );
  }

  /** modify polymorphically the target of a {@link PPrism} with a function */
  public final F modify(final F f) {
    return s -> getOrModify(s).either(Function.identity(), a -> reverseGet(f.f(a)));
  }

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

  /** set polymorphically the target of a {@link PPrism} with a value */
  public final F set(final B b) {
    return modify(Function.constant(b));
  }

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

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

  /** create a {@link Getter} from the modified target to the modified source of a {@link PPrism} */
  public final Getter re() {
    return Getter.getter(this::reverseGet);
  }

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

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

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

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

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

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

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

  /** compose a {@link PPrism} with a {@link PPrism} */
  public final  PPrism composePrism(final PPrism other) {
    return new PPrism() {

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

      @Override
      public T reverseGet(final D d) {
        return PPrism.this.reverseGet(other.reverseGet(d));
      }

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

  /** compose a {@link PPrism} with a {@link PIso} */
  public final  PPrism composeIso(final PIso other) {
    return composePrism(other.asPrism());
  }

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

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

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

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

  /** view a {@link PPrism} as a {@link PTraversal} */
  public PTraversal asTraversal() {
    final PPrism 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(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 -> getOption(s).map(f).orSome(monoid.zero());
      }

    };
  }

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

      @Override
      public Either getOrModify(final S s) {
        return self.getOrModify(s);
      }

      @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(final F> f) {
        return self.modifyValidationF(f);
      }

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

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

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

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

    };
  }

  public static  PPrism pId() {
    return PIso. pId().asPrism();
  }

  /** create a {@link PPrism} using the canonical functions: getOrModify and reverseGet */
  public static  PPrism pPrism(final F> getOrModify, final F reverseGet) {
    return new PPrism() {

      @Override
      public Either getOrModify(final S s) {
        return getOrModify.f(s);
      }

      @Override
      public T reverseGet(final B b) {
        return reverseGet.f(b);
      }

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

}