package com.codepoetics.octarine.functional.lenses; import org.pcollections.PMap; import org.pcollections.PVector; import java.util.Arrays; import java.util.Optional; import java.util.function.BiFunction; import java.util.function.Function; public interface Lens<T, V> { public static <T, V> Lens<T, V> of(Function<T, V> getter, BiFunction<T, V, T> setter) { return new Lens<T, V>() { @Override public V get(T target) { return getter.apply(target); } @Override public T set(T target, V newValue) { return setter.apply(target, newValue); } }; } static <T> Lens<T[], T> intoArray(int index) { return of( ts -> ts[index], (ts, t) -> { T[] copy = Arrays.copyOf(ts, ts.length); copy[index] = t; return copy; } ); } static <K, V> Lens<PMap<K, V>, V> intoPMap(K key) { return of( m -> m.get(key), (m, v) -> m.plus(key, v) ); } static <T> Lens<PVector<T>, T> intoPVector(int index) { return of( ts -> ts.get(index), (ts, t) -> ts.with(index, t) ); } V get(T instance); T set(T instance, V newValue); default OptionalLens<T, V> asOptional() { return OptionalLens.wrap(Lens.<T, Optional<V>>of( t -> Optional.ofNullable(get(t)), (t, v) -> set(t, v.orElse(null)) )); } default T update(T instance, Function<V, V> updater) { return set(instance, updater.apply(get(instance))); } default Function<T, T> inject(V newValue) { return t -> set(t, newValue); } default Function<T, T> inflect(Function<V, V> updater) { return t -> update(t, updater); } default <V2> Lens<T, V2> join(Lens<V, V2> next) { return Lens.of( t -> next.get(get(t)), (t, v) -> set(t, next.set(get(t), v)) ); } default <V2> OptionalLens<T, V2> join(OptionalLens<V, V2> next) { return OptionalLens.of( t -> next.get(get(t)), (t, v) -> set(t, next.set(get(t), v)) ); } }