summaryrefslogtreecommitdiff
path: root/src/main/java/bjc/optics
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/bjc/optics')
-rw-r--r--src/main/java/bjc/optics/Lens.java68
-rw-r--r--src/main/java/bjc/optics/LensX.java84
-rw-r--r--src/main/java/bjc/optics/Lenses.java164
-rw-r--r--src/main/java/bjc/optics/MutableLens.java46
-rw-r--r--src/main/java/bjc/optics/Optic.java32
-rw-r--r--src/main/java/bjc/optics/PrismX.java22
-rw-r--r--src/main/java/bjc/optics/impl/package-info.java18
-rw-r--r--src/main/java/bjc/optics/package-info.java21
8 files changed, 455 insertions, 0 deletions
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 <https://www.gnu.org/licenses/>.
+ */
+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 <Whole> The item this lens can focus on
+ * @param <Part> The field this lens focuses on
+ */
+public interface Lens<Whole, Part> extends LensX<Whole, Whole, Part, Part>, BiContainer<Whole, Part, Lens<Whole, Part>> {
+ /**
+ * 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<Part> 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<Whole, Whole> 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<Whole, Whole> lift(UnaryOperator<Part> 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 <https://www.gnu.org/licenses/>.
+ */
+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 <W1> The first type the lens is used on
+ * @param <W2> The second 'whole' type
+ * @param <P1> The first 'part' type
+ * @param <P2> The second 'part' type
+ */
+public interface LensX<W1, W2, P1, P2> extends Optic<W1, W2, P1, P2> {
+ /**
+ * 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<P1, P2> action) {
+ return set(source, action.apply(get(source)));
+ }
+
+ /**
+ * Compose two type-variant lenses together.
+ *
+ * @param <V1> The first type the second lens focuses on
+ * @param <V2> 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 <V1, V2> LensX<W1, W2, V1, V2> compose(LensX<P1, P2, V1, V2> 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 <https://www.gnu.org/licenses/>.
+ */
+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 <W1> The first type the lens is used on
+ * @param <W2> The second type the lens is used on
+ * @param <P1> The first type the lens focuses on
+ * @param <P2> 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 <W1, W2, P1, P2> LensX<W1, W2, P1, P2> immutable(Function<W1, P1> getter,
+ BiFunction<W1, P2, W2> setter) {
+ return new FunctionalLensX<>(getter, setter);
+ }
+
+ /**
+ * Create a mutable lens from a pair of functions.
+ *
+ * @param <Whole> The type the lens is used on
+ * @param <Part> 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 <Whole, Part> MutableLens<Whole, Part> mutable(Function<Whole, Part> getter,
+ BiConsumer<Whole, Part> mutator) {
+ return new FunctionalMutableLens<>(getter, mutator);
+ }
+
+ /**
+ * Create a lens that reflects over the value in a holder
+ *
+ * @param <Part1> The first type contained in the holder
+ * @param <Part2> The second type contained in the holder
+ *
+ * @return A lens that focuses on the value of a holder
+ */
+ public static <Part1, Part2> LensX<Holder<Part1>, Holder<Part2>, 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<T, A> class that is isomorphic to pair
+ * instead?
+ *
+ * @param <A> The first data type
+ * @param <B> The second data type
+ * @param <T> The tag type
+ *
+ * @return A lens that operates on the value of a tagged pair.
+ */
+ public static <A, B, T> LensX<Pair<T, A>, Pair<T, B>, 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 <A> The first state type
+ * @param <B> The second state type
+ *
+ * @param val The initial state value
+ *
+ * @return A lens that focuses on the given internal state.
+ */
+ public static <A, B> LensX<?, ?, A, B> state(A val) {
+ Holder<A> 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 <A> The state type
+ *
+ * @param val The initial state value
+ *
+ * @return A lens that focuses on the given internal state.
+ */
+ public static <A> MutableLens<?, A> stateM(A val) {
+ Holder<A> hold = Holder.of(val);
+ return mutable((whole) -> hold.getValue(), (whole, vl) -> hold.replace(vl));
+ }
+}
+
+final class FunctionalLensX<W1, W2, P1, P2> implements LensX<W1, W2, P1, P2> {
+ private final BiFunction<W1, P2, W2> setter;
+ private final Function<W1, P1> getter;
+
+ FunctionalLensX(Function<W1, P1> getter, BiFunction<W1, P2, W2> 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<Whole, Part> implements MutableLens<Whole, Part> {
+ private final BiConsumer<Whole, Part> mutator;
+ private final Function<Whole, Part> getter;
+
+ FunctionalMutableLens(Function<Whole, Part> getter, BiConsumer<Whole, Part> 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 <https://www.gnu.org/licenses/>.
+ */
+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 <Whole> The type the lens is used on
+ * @param <Part> The type the lens is focused on
+ */
+public interface MutableLens<Whole, Part> extends Lens<Whole, Part> {
+ /**
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+package bjc.optics;
+
+/**
+ * General interface for optics of varying sorts
+ *
+ * @author bjcul
+ *
+ * @param <W1> The first item the optic is used on
+ * @param <W2> The second item the optic is used on
+ * @param <P1> The first item the optic focuses on
+ * @param <P2> The second item the optic focuses on
+ */
+public interface Optic<W1, W2, P1, P2> {
+ // 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 <https://www.gnu.org/licenses/>.
+ */
+package bjc.optics;
+
+public interface PrismX<W1, W2, P1, P2> extends Optic<W1, W2, P1, P2> {
+
+}
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 <https://www.gnu.org/licenses/>.
+ */
+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 <https://www.gnu.org/licenses/>.
+ */
+/**
+ *
+ */
+package bjc.optics; \ No newline at end of file