package com.codepoetics.octarine.functional.morphisms; import com.codepoetics.octarine.functional.lenses.Lens; import java.util.function.Function; public interface Bijection<A, B> extends Function<A, B> { static <A, B> Bijection<A, B> of(Function<A, B> in, Function<B, A> out) { return new Bijection<A, B>() { @Override public B apply(A input) { return in.apply(input); } @Override public Bijection<B, A> reverse() { return Bijection.<B, A>of(out, in); } }; } Bijection<B, A> reverse(); default <C> Bijection<A, C> before(Bijection<B, C> next) { return of( this.andThen(next), next.reverse().andThen(this.reverse()) ); } default <C> Bijection<C, B> after(Bijection<C, A> previous) { return previous.before(this); } default Function<A, A> mapUpdate(Function<B, B> f) { Function<B, A> reversed = reverse(); return a -> reversed.apply(f.apply(apply(a))); } default A roundTrip(A input) { return reverse().apply(apply(input)); } default <T> Lens<T, B> overTarget(Lens<T, A> lens) { return Lens.of( t -> apply(lens.get(t)), (t, v) -> lens.set(t, reverse().apply(v)) ); } default <V> Lens<A, V> overSource(Lens<B, V> lens) { return Lens.of( t -> lens.get(apply(t)), (t, v) -> reverse().apply(lens.set(apply(t), v)) ); } }