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.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 PIso} defines an isomorphism between types S, A and B, T: * * <pre> * get reverse.get * --------------------> --------------------> * S A T B * <-------------------- <-------------------- * reverse.reverseGet reverseGet * </pre> * * In addition, if f and g forms an isomorphism between `A` and `B`, i.e. if `f . g = id` and `g . f = id`, then a {@link PIso} * defines an isomorphism between `S` and `T`: * * <pre> * S T S T * | | | | * | | | | * get | | reverseGet reverse.reverseGet | | reverse.get * | | | | * | f | | g | * A --------> B A <-------- B * </pre> * * A {@link PIso} is also a valid {@link Getter}, {@link Fold}, {@link PLens}, {@link PPrism}, {@link POptional}, * {@link PTraversal} and {@link PSetter} * * @param <S> the source of a {@link PIso} * @param <T> the modified source of a {@link PIso} * @param <A> the target of a {@link PIso} * @param <B> the modified target of a {@link PIso} */ public abstract class PIso<S, T, A, B> { PIso() { super(); } /** get the target of a {@link PIso} */ public abstract A get(S s); /** get the modified source of a {@link PIso} */ public abstract T reverseGet(B b); /** reverse a {@link PIso}: the source becomes the target and the target becomes the source */ public abstract PIso<B, A, T, S> reverse(); /** modify polymorphically the target of a {@link PIso} with an Applicative function */ public final <C> F<S, F<C, T>> modifyFunctionF(final F<A, F<C, B>> f) { return s -> Function.compose(this::reverseGet, f.f(get(s))); } /** modify polymorphically the target of a {@link PIso} with an Applicative function */ public final <L> F<S, Either<L, T>> modifyEitherF(final F<A, Either<L, B>> f) { return s -> f.f(get(s)).right().map(this::reverseGet); } /** modify polymorphically the target of a {@link PIso} with an Applicative function */ public final F<S, IO<T>> modifyIOF(final F<A, IO<B>> f) { return s -> IOFunctions.map(f.f(get(s)), this::reverseGet); } /** modify polymorphically the target of a {@link PIso} with an Applicative function */ public final F<S, Trampoline<T>> modifyTrampolineF(final F<A, Trampoline<B>> f) { return s -> f.f(get(s)).map(this::reverseGet); } /** modify polymorphically the target of a {@link PIso} with an Applicative function */ public final F<S, Promise<T>> modifyPromiseF(final F<A, Promise<B>> f) { return s -> f.f(get(s)).fmap(this::reverseGet); } /** modify polymorphically the target of a {@link PIso} with an Applicative function */ public final F<S, List<T>> modifyListF(final F<A, List<B>> f) { return s -> f.f(get(s)).map(this::reverseGet); } /** modify polymorphically the target of a {@link PIso} with an Applicative function */ public final F<S, Option<T>> modifyOptionF(final F<A, Option<B>> f) { return s -> f.f(get(s)).map(this::reverseGet); } /** modify polymorphically the target of a {@link PIso} with an Applicative function */ public final F<S, Stream<T>> modifyStreamF(final F<A, Stream<B>> f) { return s -> f.f(get(s)).map(this::reverseGet); } /** modify polymorphically the target of a {@link PIso} with an Applicative function */ public final F<S, P1<T>> modifyP1F(final F<A, P1<B>> f) { return s -> f.f(get(s)).map(this::reverseGet); } /** modify polymorphically the target of a {@link PIso} with an Applicative function */ public final <E> F<S, Validation<E, T>> modifyValidationF(final F<A, Validation<E, B>> f) { return s -> f.f(get(s)).map(this::reverseGet); } /** modify polymorphically the target of a {@link PIso} with an Applicative function */ public final F<S, V2<T>> modifyV2F(final F<A, V2<B>> f) { return s -> f.f(get(s)).map(this::reverseGet); } /** modify polymorphically the target of a {@link PIso} with a function */ public final F<S, T> modify(final F<A, B> f) { return s -> reverseGet(f.f(get(s))); } /** set polymorphically the target of a {@link PIso} with a value */ public final F<S, T> set(final B b) { return Function.constant(reverseGet(b)); } /** pair two disjoint {@link PIso} */ public final <S1, T1, A1, B1> PIso<P2<S, S1>, P2<T, T1>, P2<A, A1>, P2<B, B1>> product(final PIso<S1, T1, A1, B1> other) { return pIso( ss1 -> P.p(get(ss1._1()), other.get(ss1._2())), bb1 -> P.p(reverseGet(bb1._1()), other.reverseGet(bb1._2()))); } public <C> PIso<P2<S, C>, P2<T, C>, P2<A, C>, P2<B, C>> first() { return pIso( sc -> P.p(get(sc._1()), sc._2()), bc -> P.p(reverseGet(bc._1()), bc._2())); } public <C> PIso<P2<C, S>, P2<C, T>, P2<C, A>, P2<C, B>> second() { return pIso( cs -> P.p(cs._1(), get(cs._2())), cb -> P.p(cb._1(), reverseGet(cb._2()))); } /**********************************************************/ /** Compose methods between a {@link PIso} and another Optics */ /**********************************************************/ /** compose a {@link PIso} with a {@link Fold} */ public final <C> Fold<S, C> composeFold(final Fold<A, C> other) { return asFold().composeFold(other); } /** compose a {@link PIso} with a {@link Getter} */ public final <C> Getter<S, C> composeGetter(final Getter<A, C> other) { return asGetter().composeGetter(other); } /** compose a {@link PIso} with a {@link PSetter} */ public final <C, D> PSetter<S, T, C, D> composeSetter(final PSetter<A, B, C, D> other) { return asSetter().composeSetter(other); } /** compose a {@link PIso} with a {@link PTraversal} */ public final <C, D> PTraversal<S, T, C, D> composeTraversal(final PTraversal<A, B, C, D> other) { return asTraversal().composeTraversal(other); } /** compose a {@link PIso} with a {@link POptional} */ public final <C, D> POptional<S, T, C, D> composeOptional(final POptional<A, B, C, D> other) { return asOptional().composeOptional(other); } /** compose a {@link PIso} with a {@link PPrism} */ public final <C, D> PPrism<S, T, C, D> composePrism(final PPrism<A, B, C, D> other) { return asPrism().composePrism(other); } /** compose a {@link PIso} with a {@link PLens} */ public final <C, D> PLens<S, T, C, D> composeLens(final PLens<A, B, C, D> other) { return asLens().composeLens(other); } /** compose a {@link PIso} with a {@link PIso} */ public final <C, D> PIso<S, T, C, D> composeIso(final PIso<A, B, C, D> other) { final PIso<S, T, A, B> self = this; return new PIso<S, T, C, D>() { @Override public C get(final S s) { return other.get(self.get(s)); } @Override public T reverseGet(final D d) { return self.reverseGet(other.reverseGet(d)); } @Override public PIso<D, C, T, S> reverse() { final PIso<S, T, C, D> composeSelf = this; return new PIso<D, C, T, S>() { @Override public T get(final D d) { return self.reverseGet(other.reverseGet(d)); } @Override public C reverseGet(final S s) { return other.get(self.get(s)); } @Override public PIso<S, T, C, D> reverse() { return composeSelf; } }; } }; } /****************************************************************/ /** Transformation methods to view a {@link PIso} as another Optics */ /****************************************************************/ /** view a {@link PIso} as a {@link Fold} */ public final Fold<S, A> asFold() { return new Fold<S, A>() { @Override public <M> F<S, M> foldMap(final Monoid<M> m, final F<A, M> f) { return s -> f.f(PIso.this.get(s)); } }; } /** view a {@link PIso} as a {@link Getter} */ public final Getter<S, A> asGetter() { return new Getter<S, A>() { @Override public A get(final S s) { return PIso.this.get(s); } }; } /** view a {@link PIso} as a {@link Setter} */ public PSetter<S, T, A, B> asSetter() { return new PSetter<S, T, A, B>() { @Override public F<S, T> modify(final F<A, B> f) { return PIso.this.modify(f); } @Override public F<S, T> set(final B b) { return PIso.this.set(b); } }; } /** view a {@link PIso} as a {@link PTraversal} */ public PTraversal<S, T, A, B> asTraversal() { final PIso<S, T, A, B> self = this; return new PTraversal<S, T, A, B>() { @Override public <C> F<S, F<C, T>> modifyFunctionF(final F<A, F<C, B>> f) { return self.modifyFunctionF(f); } @Override public <L> F<S, Either<L, T>> modifyEitherF(final F<A, Either<L, B>> f) { return self.modifyEitherF(f); } @Override public F<S, IO<T>> modifyIOF(final F<A, IO<B>> f) { return self.modifyIOF(f); } @Override public F<S, Trampoline<T>> modifyTrampolineF(final F<A, Trampoline<B>> f) { return self.modifyTrampolineF(f); } @Override public F<S, Promise<T>> modifyPromiseF(final F<A, Promise<B>> f) { return self.modifyPromiseF(f); } @Override public F<S, List<T>> modifyListF(final F<A, List<B>> f) { return self.modifyListF(f); } @Override public F<S, Option<T>> modifyOptionF(final F<A, Option<B>> f) { return self.modifyOptionF(f); } @Override public F<S, Stream<T>> modifyStreamF(final F<A, Stream<B>> f) { return self.modifyStreamF(f); } @Override public F<S, P1<T>> modifyP1F(final F<A, P1<B>> f) { return self.modifyP1F(f); } @Override public <E> F<S, Validation<E, T>> modifyValidationF(Semigroup<E> s, final F<A, Validation<E, B>> f) { return self.modifyValidationF(f); } @Override public F<S, V2<T>> modifyV2F(final F<A, V2<B>> f) { return self.modifyV2F(f); } @Override public <M> F<S, M> foldMap(final Monoid<M> monoid, final F<A, M> f) { return s -> f.f(self.get(s)); } }; } /** view a {@link PIso} as a {@link POptional} */ public POptional<S, T, A, B> asOptional() { final PIso<S, T, A, B> self = this; return new POptional<S, T, A, B>() { @Override public Either<T, A> getOrModify(final S s) { return Either.right(self.get(s)); } @Override public <C> F<S, F<C, T>> modifyFunctionF(final F<A, F<C, B>> f) { return self.modifyFunctionF(f); } @Override public <L> F<S, Either<L, T>> modifyEitherF(final F<A, Either<L, B>> f) { return self.modifyEitherF(f); } @Override public F<S, IO<T>> modifyIOF(final F<A, IO<B>> f) { return self.modifyIOF(f); } @Override public F<S, Trampoline<T>> modifyTrampolineF(final F<A, Trampoline<B>> f) { return self.modifyTrampolineF(f); } @Override public F<S, Promise<T>> modifyPromiseF(final F<A, Promise<B>> f) { return self.modifyPromiseF(f); } @Override public F<S, List<T>> modifyListF(final F<A, List<B>> f) { return self.modifyListF(f); } @Override public F<S, Option<T>> modifyOptionF(final F<A, Option<B>> f) { return self.modifyOptionF(f); } @Override public F<S, Stream<T>> modifyStreamF(final F<A, Stream<B>> f) { return self.modifyStreamF(f); } @Override public F<S, P1<T>> modifyP1F(final F<A, P1<B>> f) { return self.modifyP1F(f); } @Override public <E> F<S, Validation<E, T>> modifyValidationF(final F<A, Validation<E, B>> f) { return self.modifyValidationF(f); } @Override public F<S, V2<T>> modifyV2F(final F<A, V2<B>> f) { return self.modifyV2F(f); } @Override public F<S, T> set(final B b) { return self.set(b); } @Override public Option<A> getOption(final S s) { return Option.some(self.get(s)); } @Override public F<S, T> modify(final F<A, B> f) { return self.modify(f); } }; } /** view a {@link PIso} as a {@link PPrism} */ public PPrism<S, T, A, B> asPrism() { final PIso<S, T, A, B> self = this; return new PPrism<S, T, A, B>() { @Override public Either<T, A> getOrModify(final S s) { return Either.right(self.get(s)); } @Override public T reverseGet(final B b) { return self.reverseGet(b); } @Override public Option<A> getOption(final S s) { return Option.some(self.get(s)); } }; } /** view a {@link PIso} as a {@link PLens} */ public PLens<S, T, A, B> asLens() { final PIso<S, T, A, B> self = this; return new PLens<S, T, A, B>() { @Override public A get(final S s) { return self.get(s); } @Override public F<S, T> set(final B b) { return self.set(b); } @Override public F<S, T> modify(final F<A, B> f) { return self.modify(f); } @Override public <C> F<S, F<C, T>> modifyFunctionF(final F<A, F<C, B>> f) { return self.modifyFunctionF(f); } @Override public <L> F<S, Either<L, T>> modifyEitherF(final F<A, Either<L, B>> f) { return self.modifyEitherF(f); } @Override public F<S, IO<T>> modifyIOF(final F<A, IO<B>> f) { return self.modifyIOF(f); } @Override public F<S, Trampoline<T>> modifyTrampolineF(final F<A, Trampoline<B>> f) { return self.modifyTrampolineF(f); } @Override public F<S, Promise<T>> modifyPromiseF(final F<A, Promise<B>> f) { return self.modifyPromiseF(f); } @Override public F<S, List<T>> modifyListF(final F<A, List<B>> f) { return self.modifyListF(f); } @Override public F<S, Option<T>> modifyOptionF(final F<A, Option<B>> f) { return self.modifyOptionF(f); } @Override public F<S, Stream<T>> modifyStreamF(final F<A, Stream<B>> f) { return self.modifyStreamF(f); } @Override public F<S, P1<T>> modifyP1F(final F<A, P1<B>> f) { return self.modifyP1F(f); } @Override public <E> F<S, Validation<E, T>> modifyValidationF(final F<A, Validation<E, B>> f) { return self.modifyValidationF(f); } @Override public F<S, V2<T>> modifyV2F(final F<A, V2<B>> f) { return self.modifyV2F(f); } }; } /** create a {@link PIso} using a pair of functions: one to get the target and one to get the source. */ public static <S, T, A, B> PIso<S, T, A, B> pIso(final F<S, A> get, final F<B, T> reverseGet) { return new PIso<S, T, A, B>() { @Override public A get(final S s) { return get.f(s); } @Override public T reverseGet(final B b) { return reverseGet.f(b); } @Override public PIso<B, A, T, S> reverse() { final PIso<S, T, A, B> self = this; return new PIso<B, A, T, S>() { @Override public T get(final B b) { return reverseGet.f(b); } @Override public A reverseGet(final S s) { return get.f(s); } @Override public PIso<S, T, A, B> reverse() { return self; } }; } }; } /** * create a {@link PIso} between any type and itself. id is the zero element of optics composition, for all optics o of type O * (e.g. Lens, Iso, Prism, ...): * * <pre> * o composeIso Iso.id == o * Iso.id composeO o == o * </pre> * * (replace composeO by composeLens, composeIso, composePrism, ...) */ public static <S, T> PIso<S, T, S, T> pId() { return new PIso<S, T, S, T>() { @Override public S get(final S s) { return s; } @Override public T reverseGet(final T t) { return t; } @Override public PIso<T, S, T, S> reverse() { final PIso<S, T, S, T> self = this; return new PIso<T, S, T, S>() { @Override public T get(final T t) { return t; } @Override public S reverseGet(final S s) { return s; } @Override public PIso<S, T, S, T> reverse() { return self; } }; } }; } }