package fj.data.optic;
import fj.F;
import fj.Function;
import fj.data.Either;
/**
* A {@link PSetter} is a generalisation of Functor map: - `map: (A => B) => F[A] => F[B]` - `modify: (A => B) => S =>
* T`
*
* {@link PSetter} stands for Polymorphic Setter as it set and modify methods change a type `A` to `B` and `S` to `T`.
*
* {@link PTraversal}, {@link POptional}, {@link PPrism}, {@link PLens} and {@link PIso} are valid {@link PSetter}
*
* @param <S> the source of a {@link PSetter}
* @param <T> the modified source of a {@link PSetter}
* @param <A> the target of a {@link PSetter}
* @param <B> the modified target of a {@link PSetter}
*/
public abstract class PSetter<S, T, A, B> {
PSetter() {
super();
}
/** modify polymorphically the target of a {@link PSetter} with a function */
public abstract F<S, T> modify(F<A, B> f);
/** set polymorphically the target of a {@link PSetter} with a value */
public abstract F<S, T> set(final B b);
/** join two {@link PSetter} with the same target */
public final <S1, T1> PSetter<Either<S, S1>, Either<T, T1>, A, B> sum(final PSetter<S1, T1, A, B> other) {
return pSetter(f -> e -> e.bimap(modify(f), other.modify(f)));
}
/*************************************************************/
/** Compose methods between a {@link PSetter} and another Optics */
/*************************************************************/
/** compose a {@link PSetter} with a {@link PSetter} */
public final <C, D> PSetter<S, T, C, D> composeSetter(final PSetter<A, B, C, D> other) {
final PSetter<S, T, A, B> self = this;
return new PSetter<S, T, C, D>() {
@Override
public F<S, T> modify(final F<C, D> f) {
return self.modify(other.modify(f));
}
@Override
public F<S, T> set(final D d) {
return self.modify(other.set(d));
}
};
}
/** compose a {@link PSetter} with a {@link PTraversal} */
public final <C, D> PSetter<S, T, C, D> composeTraversal(final PTraversal<A, B, C, D> other) {
return composeSetter(other.asSetter());
}
/** compose a {@link PSetter} with a {@link POptional} */
public final <C, D> PSetter<S, T, C, D> composeOptional(final POptional<A, B, C, D> other) {
return composeSetter(other.asSetter());
}
/** compose a {@link PSetter} with a {@link PPrism} */
public final <C, D> PSetter<S, T, C, D> composePrism(final PPrism<A, B, C, D> other) {
return composeSetter(other.asSetter());
}
/** compose a {@link PSetter} with a {@link PLens} */
public final <C, D> PSetter<S, T, C, D> composeLens(final PLens<A, B, C, D> other) {
return composeSetter(other.asSetter());
}
/** compose a {@link PSetter} with a {@link PIso} */
public final <C, D> PSetter<S, T, C, D> composeIso(final PIso<A, B, C, D> other) {
return composeSetter(other.asSetter());
}
public static <S, T> PSetter<S, T, S, T> pId() {
return PIso.<S, T> pId().asSetter();
}
public static <S, T> PSetter<Either<S, S>, Either<T, T>, S, T> pCodiagonal() {
return pSetter(f -> e -> e.bimap(f, f));
}
public static <S, T, A, B> PSetter<S, T, A, B> pSetter(final F<F<A, B>, F<S, T>> modify) {
return new PSetter<S, T, A, B>() {
@Override
public F<S, T> modify(final F<A, B> f) {
return modify.f(f);
}
@Override
public F<S, T> set(final B b) {
return modify.f(Function.constant(b));
}
};
}
}