From 0f958b08b3446a866418aa485bb60c208d952033 Mon Sep 17 00:00:00 2001 From: Ben Culkin Date: Sat, 8 Oct 2022 14:47:21 -0400 Subject: Add a whole bunch of Optics This adds a whole bunch of types/functions related to optics. With this batch, I've mainly gone for the concrete representation types, instead of var Laarhoven or profunctor representations. The concrete ones require less infrastructure, though they are not as easy to compose There's also a number of misc. things in here --- src/main/java/bjc/optics/Lens.java | 68 ++++++++++ src/main/java/bjc/optics/LensX.java | 84 ++++++++++++ src/main/java/bjc/optics/Lenses.java | 164 ++++++++++++++++++++++++ src/main/java/bjc/optics/MutableLens.java | 46 +++++++ src/main/java/bjc/optics/Optic.java | 32 +++++ src/main/java/bjc/optics/PrismX.java | 22 ++++ src/main/java/bjc/optics/impl/package-info.java | 18 +++ src/main/java/bjc/optics/package-info.java | 21 +++ 8 files changed, 455 insertions(+) create mode 100644 src/main/java/bjc/optics/Lens.java create mode 100644 src/main/java/bjc/optics/LensX.java create mode 100644 src/main/java/bjc/optics/Lenses.java create mode 100644 src/main/java/bjc/optics/MutableLens.java create mode 100644 src/main/java/bjc/optics/Optic.java create mode 100644 src/main/java/bjc/optics/PrismX.java create mode 100644 src/main/java/bjc/optics/impl/package-info.java create mode 100644 src/main/java/bjc/optics/package-info.java (limited to 'src/main/java/bjc/optics') diff --git a/src/main/java/bjc/optics/Lens.java b/src/main/java/bjc/optics/Lens.java new file mode 100644 index 0000000..be10eef --- /dev/null +++ b/src/main/java/bjc/optics/Lens.java @@ -0,0 +1,68 @@ +/* + * 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.Function; +import java.util.function.UnaryOperator; + +import bjc.typeclasses.BiContainer; + +/** + * A type-invariant var Laarhoven lens. + * + * @author bjcul + * + * @param The item this lens can focus on + * @param The field this lens focuses on + */ +public interface Lens extends LensX, BiContainer> { + /** + * Modify a given whole using an operation + * + * @param source The whole to modify. + * @param mod The operation to use for modifying a part + * + * @return A modified whole + */ + default Whole modify(Whole source, UnaryOperator mod) { + return set(source, mod.apply(get(source))); + } + + /** + * Create a function which sets the part of a given whole. + * + * @param part The part to set + * + * @return A function that sets the given part on a whole. + */ + default Function setting(Part part) { + return (whole) -> set(whole, part); + } + + /** + * Lift a function that modifies parts to one that modifies wholes. + * + * @param f The function which operates on parts + * + * @return A corresponding function which applies the given modification to a part. + */ + default Function lift(UnaryOperator f) { + // modify will be more efficient for some lenses + return (whole) -> modify(whole, f); + } +} diff --git a/src/main/java/bjc/optics/LensX.java b/src/main/java/bjc/optics/LensX.java new file mode 100644 index 0000000..d0f83be --- /dev/null +++ b/src/main/java/bjc/optics/LensX.java @@ -0,0 +1,84 @@ +/* + * 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 static bjc.optics.Lenses.immutable; + +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * A type-variant lens + * + * @author Ben Culkin + * + * @param The first type the lens is used on + * @param The second 'whole' type + * @param The first 'part' type + * @param The second 'part' type + */ +public interface LensX extends Optic { + /** + * Retrieve the focused value of this lens. + * + * @param source The item to use the lens on. + * + * @return The value from the given whole this lens focuses on. + */ + P1 get(W1 source); + + /** + * Create an updated version of the item this lens focuses on. + * + * @param source The item to use the lens on. + * @param val The new value. + * + * @return The updated item the lens was used on. + */ + W2 set(W1 source, P2 val); + + /** + * Update the focused value. + * + * NOTE: It will often be more efficient to implement this directly. The + * implementation here is provided for convenience. + * + * @param source The item to use the lens on. + * @param action The action to applied to the focused item. + * + * @return The updated item the lens was used on. + */ + default W2 update(W1 source, Function action) { + return set(source, action.apply(get(source))); + } + + /** + * Compose two type-variant lenses together. + * + * @param The first type the second lens focuses on + * @param The second type the second lens focuses on. + * + * @param other The second lens to use. + * + * @return A lens composed from this one and the given one. + */ + default LensX compose(LensX other) { + return immutable((whole) -> other.get(get(whole)), + (whole, val) -> update(whole, (P1 val2) -> other.set(val2, val))); + } +} \ No newline at end of file diff --git a/src/main/java/bjc/optics/Lenses.java b/src/main/java/bjc/optics/Lenses.java new file mode 100644 index 0000000..6cebc84 --- /dev/null +++ b/src/main/java/bjc/optics/Lenses.java @@ -0,0 +1,164 @@ +/* + * 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); + } +} diff --git a/src/main/java/bjc/optics/MutableLens.java b/src/main/java/bjc/optics/MutableLens.java new file mode 100644 index 0000000..88eca04 --- /dev/null +++ b/src/main/java/bjc/optics/MutableLens.java @@ -0,0 +1,46 @@ +/* + * 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; + +/** + * A type-invariant lens for mutating objects. + * + * Note that there is no type-variant version, because that wouldn't make much sense. + * + * Also, mixing mutable and immutable lenses may lead to confusion. + * + * @author bjcul + * + * @param The type the lens is used on + * @param The type the lens is focused on + */ +public interface MutableLens extends Lens { + /** + * Apply a mutation to an item. + * + * @param source The item to use the lens on. + * @param val The new value for the focused field. + */ + void mutate(Whole source, Part val); + + @Override + default Whole set(Whole source, Part val) { + mutate(source, val); + return source; + } +} diff --git a/src/main/java/bjc/optics/Optic.java b/src/main/java/bjc/optics/Optic.java new file mode 100644 index 0000000..389da21 --- /dev/null +++ b/src/main/java/bjc/optics/Optic.java @@ -0,0 +1,32 @@ +/* + * 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; + +/** + * General interface for optics of varying sorts + * + * @author bjcul + * + * @param The first item the optic is used on + * @param The second item the optic is used on + * @param The first item the optic focuses on + * @param The second item the optic focuses on + */ +public interface Optic { + // Marker interface +} diff --git a/src/main/java/bjc/optics/PrismX.java b/src/main/java/bjc/optics/PrismX.java new file mode 100644 index 0000000..b4986cf --- /dev/null +++ b/src/main/java/bjc/optics/PrismX.java @@ -0,0 +1,22 @@ +/* + * 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; + +public interface PrismX extends Optic { + +} diff --git a/src/main/java/bjc/optics/impl/package-info.java b/src/main/java/bjc/optics/impl/package-info.java new file mode 100644 index 0000000..b147877 --- /dev/null +++ b/src/main/java/bjc/optics/impl/package-info.java @@ -0,0 +1,18 @@ +/* + * 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.impl; \ No newline at end of file diff --git a/src/main/java/bjc/optics/package-info.java b/src/main/java/bjc/optics/package-info.java new file mode 100644 index 0000000..241b565 --- /dev/null +++ b/src/main/java/bjc/optics/package-info.java @@ -0,0 +1,21 @@ +/* + * 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; \ No newline at end of file -- cgit v1.2.3