package fj.data.optic;
import fj.F;
import fj.Function;
import fj.Monoid;
import fj.P;
import fj.P2;
import fj.data.Either;
/**
* A {@link Getter} can be seen as a glorified get method between a type S and a type A.
*
* A {@link Getter} is also a valid {@link Fold}
*
* @param <S> the source of a {@link Getter}
* @param <A> the target of a {@link Getter}
*/
public abstract class Getter<S, A> {
Getter() {
super();
}
/** get the target of a {@link Getter} */
public abstract A get(S s);
/** join two {@link Getter} with the same target */
public final <S1> Getter<Either<S, S1>, A> sum(final Getter<S1, A> other) {
return getter(e -> e.either(this::get, other::get));
}
/** pair two disjoint {@link Getter} */
public final <S1, A1> Getter<P2<S, S1>, P2<A, A1>> product(final Getter<S1, A1> other) {
return getter(p2 -> P.p(this.get(p2._1()), other.get(p2._2())));
}
public final <B> Getter<P2<S, B>, P2<A, B>> first() {
return getter(p -> P.p(this.get(p._1()), p._2()));
}
public final <B> Getter<P2<B, S>, P2<B, A>> second() {
return getter(p -> P.p(p._1(), this.get(p._2())));
}
/*************************************************************/
/** Compose methods between a {@link Getter} and another Optics */
/*************************************************************/
/** compose a {@link Getter} with a {@link Fold} */
public final <B> Fold<S, B> composeFold(final Fold<A, B> other) {
return asFold().composeFold(other);
}
/** compose a {@link Getter} with a {@link Getter} */
public final <B> Getter<S, B> composeGetter(final Getter<A, B> other) {
return getter(s -> other.get(get(s)));
}
/** compose a {@link Getter} with a {@link POptional} */
public final <B, C, D> Fold<S, C> composeOptional(final POptional<A, B, C, D> other) {
return asFold().composeOptional(other);
}
/** compose a {@link Getter} with a {@link PPrism} */
public final <B, C, D> Fold<S, C> composePrism(final PPrism<A, B, C, D> other) {
return asFold().composePrism(other);
}
/** compose a {@link Getter} with a {@link PLens} */
public final <B, C, D> Getter<S, C> composeLens(final PLens<A, B, C, D> other) {
return composeGetter(other.asGetter());
}
/** compose a {@link Getter} with a {@link PIso} */
public final <B, C, D> Getter<S, C> composeIso(final PIso<A, B, C, D> other) {
return composeGetter(other.asGetter());
}
/******************************************************************/
/** Transformation methods to view a {@link Getter} as another Optics */
/******************************************************************/
/** view a {@link Getter} with a {@link Fold} */
public final Fold<S, A> asFold() {
return new Fold<S, A>() {
@Override
public <B> F<S, B> foldMap(final Monoid<B> m, final F<A, B> f) {
return s -> f.f(get(s));
}
};
}
public static <A> Getter<A, A> id() {
return PIso.<A, A> pId().asGetter();
}
public static <A> Getter<Either<A, A>, A> codiagonal() {
return getter(e -> e.either(Function.identity(), Function.identity()));
}
public static <S, A> Getter<S, A> getter(final F<S, A> get) {
return new Getter<S, A>() {
@Override
public A get(final S s) {
return get.f(s);
}
};
}
}