/* * esodata - data structures and other things, of varying utility * Copyright 2022, Ben Culkin * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package bjc.optics; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Function; import bjc.data.Holder; import bjc.data.Pair; /** * Utility and constructor functions for lenses * * @author bjcul * */ public class Lenses { /** * Create an immutable lens from a pair of functions. * * @param The first type the lens is used on * @param The second type the lens is used on * @param The first type the lens focuses on * @param The second type the lens focuses on. * * @param getter The getter for the lens * @param setter The setter for the lens * * @return The lens composed from the two given functions */ public static LensX immutable(Function getter, BiFunction setter) { return new FunctionalLensX<>(getter, setter); } /** * Create a mutable lens from a pair of functions. * * @param The type the lens is used on * @param The type the lens is focused on * * @param getter The getter for the lens * @param mutator The mutator for the lens * * @return The mutable lens composed from the two given functions */ public static MutableLens mutable(Function getter, BiConsumer mutator) { return new FunctionalMutableLens<>(getter, mutator); } /** * Create a lens that reflects over the value in a holder * * @param The first type contained in the holder * @param The second type contained in the holder * * @return A lens that focuses on the value of a holder */ public static LensX, Holder, Part1, Part2> holder() { return immutable((hld) -> hld.getValue(), (hld, val) -> hld.map((vl) -> val)); } /** * Create a lens for updating tagged pairs. * * TODO: Should this be on a Tagged class that is isomorphic to pair * instead? * * @param The first data type * @param The second data type * @param The tag type * * @return A lens that operates on the value of a tagged pair. */ public static LensX, Pair, A, B> tagged() { return immutable((par) -> par.getRight(), (par, val) -> par.mapRight((vl) -> val)); } /** * Creates a lens which focuses on a piece of internal state. * * @param The first state type * @param The second state type * * @param val The initial state value * * @return A lens that focuses on the given internal state. */ public static LensX state(A val) { Holder hold = Holder.of(val); return immutable((whole) -> hold.getValue(), (whole, vl) -> hold.map((arg) -> vl)); } /** * Creates a lens which focuses on a piece of mutable internal state. * * @param The state type * * @param val The initial state value * * @return A lens that focuses on the given internal state. */ public static MutableLens stateM(A val) { Holder hold = Holder.of(val); return mutable((whole) -> hold.getValue(), (whole, vl) -> hold.replace(vl)); } } final class FunctionalLensX implements LensX { private final BiFunction setter; private final Function getter; FunctionalLensX(Function getter, BiFunction setter) { this.setter = setter; this.getter = getter; } @Override public P1 get(W1 source) { return getter.apply(source); } @Override public W2 set(W1 source, P2 val) { return setter.apply(source, val); } } final class FunctionalMutableLens implements MutableLens { private final BiConsumer mutator; private final Function getter; FunctionalMutableLens(Function getter, BiConsumer mutator) { this.mutator = mutator; this.getter = getter; } @Override public Part get(Whole source) { return getter.apply(source); } @Override public void mutate(Whole source, Part val) { mutator.accept(source, val); } }