summaryrefslogtreecommitdiff
path: root/src/main/java/bjc/optics
diff options
context:
space:
mode:
authorBen Culkin <scorpress@gmail.com>2023-06-25 15:50:38 -0400
committerBen Culkin <scorpress@gmail.com>2023-06-25 15:50:38 -0400
commit44be6e6cd7671dd243056107ffa6201504f7fbce (patch)
treeea6d1d0bf5930395c3b79b40b4889782dc1b5791 /src/main/java/bjc/optics
parent0f958b08b3446a866418aa485bb60c208d952033 (diff)
Update a number of things
Diffstat (limited to 'src/main/java/bjc/optics')
-rw-r--r--src/main/java/bjc/optics/Adapter.java15
-rw-r--r--src/main/java/bjc/optics/AdapterX.java89
-rw-r--r--src/main/java/bjc/optics/Adapters.java47
-rw-r--r--src/main/java/bjc/optics/Lens.java38
-rw-r--r--src/main/java/bjc/optics/LensX.java1
-rw-r--r--src/main/java/bjc/optics/Lenses.java23
-rw-r--r--src/main/java/bjc/optics/Prism.java70
-rw-r--r--src/main/java/bjc/optics/PrismX.java67
-rw-r--r--src/main/java/bjc/optics/Prisms.java56
-rw-r--r--src/main/java/bjc/optics/Traversal.java14
-rw-r--r--src/main/java/bjc/optics/TraversalX.java54
11 files changed, 462 insertions, 12 deletions
diff --git a/src/main/java/bjc/optics/Adapter.java b/src/main/java/bjc/optics/Adapter.java
new file mode 100644
index 0000000..8ad082f
--- /dev/null
+++ b/src/main/java/bjc/optics/Adapter.java
@@ -0,0 +1,15 @@
+package bjc.optics;
+
+import bjc.typeclasses.BiContainer;
+
+/**
+ * A type-invariant adapter
+ * @author bjcul
+ *
+ * @param <From> The source type
+ * @param <To> The destination type
+ */
+public interface Adapter<From, To>
+ extends AdapterX<From, From, To, To>, BiContainer<From, To, Adapter<From, To>> {
+ // TODO: write 'of' function
+}
diff --git a/src/main/java/bjc/optics/AdapterX.java b/src/main/java/bjc/optics/AdapterX.java
new file mode 100644
index 0000000..f037508
--- /dev/null
+++ b/src/main/java/bjc/optics/AdapterX.java
@@ -0,0 +1,89 @@
+/*
+ * esodata - data structures 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;
+
+/**
+ * An adapter that maps between two sets of types
+ *
+ * @author bjcul
+ *
+ * @param <F1> The type of the first source
+ * @param <F2> The type of the second source
+ * @param <T1> The type of the first destination
+ * @param <T2> The type of the second destination
+ */
+public interface AdapterX<F1, F2, T1, T2> extends Optic<F1, F2, T1, T2> {
+
+ /**
+ * Create a source from a destination
+ *
+ * @param source The destination to use
+ *
+ * @return A source corresponding to the destination
+ */
+ F1 from(T1 source);
+
+ /**
+ * Create a destination from a source
+ *
+ * @param source The source to use
+ *
+ * @return A destination corresponding to the source
+ */
+ T2 to(F2 source);
+
+ /**
+ * Create an adapter from its component parts
+ *
+ * @param <F1> The type of the first source
+ * @param <F2> The type of the second source
+ * @param <T1> The type of the first destination
+ * @param <T2> The type of the second destination
+ *
+ * @param f The 'from' function
+ * @param g The 'to' function
+ *
+ * @return The adapter constructed from the given parts.
+ */
+ public static <F1, F2, T1, T2> AdapterX<F1, F2, T1, T2> of(Function<T1, F1> f, Function<F2, T2> g) {
+ return new FunctionalAdapterX<>(f, g);
+ }
+}
+
+final class FunctionalAdapterX<W1, W2, P1, P2> implements AdapterX<W1, W2, P1, P2> {
+ private final Function<P1, W1> f;
+ private final Function<W2, P2> g;
+
+ public FunctionalAdapterX(Function<P1, W1> f, Function<W2, P2> g) {
+ this.f = f;
+ this.g = g;
+ }
+
+ @Override
+ public W1 from(P1 source) {
+ return f.apply(source);
+ }
+
+ @Override
+ public P2 to(W2 source) {
+ return g.apply(source);
+ }
+}
diff --git a/src/main/java/bjc/optics/Adapters.java b/src/main/java/bjc/optics/Adapters.java
new file mode 100644
index 0000000..13a9d02
--- /dev/null
+++ b/src/main/java/bjc/optics/Adapters.java
@@ -0,0 +1,47 @@
+/*
+ * esodata - data structures of varying use
+ * 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 bjc.data.Pair;
+import bjc.data.Triple;
+
+/**
+ * Various utilities for dealing with adapters
+ *
+ * @author bjcul
+ *
+ */
+public class Adapters {
+ /**
+ * Create an adapter between triples and left-nested pairs.
+ *
+ * @param <A1> The first left type
+ * @param <A2> The second left type
+ * @param <B1> The first middle type
+ * @param <B2> The second middle type
+ * @param <C1> The first right type
+ * @param <C2> The second right type
+ *
+ * @return An adapter between triples and left-nested pairs
+ */
+ public static <A1, A2, B1, B2, C1, C2> AdapterX<Triple<A1, B1, C1>, Triple<A2, B2, C2>, Pair<Pair<A1, B1>, C1>,
+ Pair<Pair<A2, B2>, C2>> flatten() {
+ return AdapterX.of((par) -> Triple.of(par.getLeft().getLeft(), par.getLeft().getRight(), par.getRight()),
+ (trp) -> Pair.pair(Pair.pair(trp.left(), trp.middle()), trp.right()));
+ }
+}
diff --git a/src/main/java/bjc/optics/Lens.java b/src/main/java/bjc/optics/Lens.java
index be10eef..e8cab84 100644
--- a/src/main/java/bjc/optics/Lens.java
+++ b/src/main/java/bjc/optics/Lens.java
@@ -17,8 +17,9 @@
*/
package bjc.optics;
-import java.util.function.Function;
-import java.util.function.UnaryOperator;
+import static bjc.optics.Lenses.immutable;
+
+import java.util.function.*;
import bjc.typeclasses.BiContainer;
@@ -28,21 +29,22 @@ import bjc.typeclasses.BiContainer;
* @author bjcul
*
* @param <Whole> The item this lens can focus on
- * @param <Part> The field this lens focuses 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>> {
+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
+ * @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.
*
@@ -53,16 +55,36 @@ public interface Lens<Whole, Part> extends LensX<Whole, Whole, Part, Part>, BiCo
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.
+ * @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);
}
+
+ /**
+ * 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> Lens<Whole, V1> compose(Lens<Part, V1> other) {
+ LensX<Whole, Whole, V1, V1> lensX = immutable((whole) -> other.get(get(whole)),
+ (whole, part) -> update(whole, (val2) -> other.set(val2, part)));
+ // Note: if lensX is inlined, then the setter function for the lens has to be
+ // externalized, otherwise type-inference goes BOOM and everything fails.
+ // Should ask on stack overflow why that is
+ return (Lens<Whole, V1>) lensX;
+ }
}
diff --git a/src/main/java/bjc/optics/LensX.java b/src/main/java/bjc/optics/LensX.java
index d0f83be..02777e2 100644
--- a/src/main/java/bjc/optics/LensX.java
+++ b/src/main/java/bjc/optics/LensX.java
@@ -19,7 +19,6 @@ package bjc.optics;
import static bjc.optics.Lenses.immutable;
-import java.util.function.BiFunction;
import java.util.function.Function;
/**
diff --git a/src/main/java/bjc/optics/Lenses.java b/src/main/java/bjc/optics/Lenses.java
index 6cebc84..aad5571 100644
--- a/src/main/java/bjc/optics/Lenses.java
+++ b/src/main/java/bjc/optics/Lenses.java
@@ -76,7 +76,7 @@ public class Lenses {
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.
*
@@ -92,7 +92,13 @@ public class Lenses {
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));
}
-
+
+ // Not entirely sure what I was thinking when I wrote this. Pretty sure this
+ // would need W1 to be a monoid to make much sense
+ // public static <W1, P1, P2> Lens<W1, Pair<P1, P2>> both(Lens<W1, P1> lhs,
+ // Lens<W1, P2> rhs) {
+ // }
+
/**
* Creates a lens which focuses on a piece of internal state.
*
@@ -107,7 +113,7 @@ public class Lenses {
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.
*
@@ -121,6 +127,17 @@ public class Lenses {
Holder<A> hold = Holder.of(val);
return mutable((whole) -> hold.getValue(), (whole, vl) -> hold.replace(vl));
}
+
+ /**
+ * Create a lens that focuses on the sign of an integer
+ *
+ * @return A lens that focuses on the sign of an integer
+ */
+ public static Lens<Integer, Boolean> sign() {
+ LensX<Integer, Integer, Boolean,
+ Boolean> lensX = immutable((num) -> num >= 0, (num, sgn) -> sgn ? Math.abs(num) : -Math.abs(num));
+ return (Lens<Integer, Boolean>) lensX;
+ }
}
final class FunctionalLensX<W1, W2, P1, P2> implements LensX<W1, W2, P1, P2> {
diff --git a/src/main/java/bjc/optics/Prism.java b/src/main/java/bjc/optics/Prism.java
new file mode 100644
index 0000000..a04939b
--- /dev/null
+++ b/src/main/java/bjc/optics/Prism.java
@@ -0,0 +1,70 @@
+/*
+ * esodata - data structures 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 bjc.data.Either;
+import bjc.typeclasses.BiContainer;
+
+/**
+ * A type-invariant prism
+ *
+ * @author bjcul
+ *
+ * @param <Whole> The type of the whole
+ * @param <Part> The type of the part
+ */
+public interface Prism<Whole, Part>
+ extends PrismX<Whole, Whole, Part, Part>, BiContainer<Whole, Part, Prism<Whole, Part>> {
+
+ /**
+ * Create a type-invariant prism from its component parts.
+ *
+ * @param <Whole> The type of the whole
+ * @param <Part> The type of the part
+ *
+ * @param f The 'match' function of the prism
+ * @param g The 'build' function of the prism
+ *
+ * @return The prism composed of the given parts.
+ */
+ static <Whole, Part> Prism<Whole, Part> of(Function<Part, Either<Part, Whole>> f, Function<Whole, Part> g) {
+ return new FunctionalPrism<>(g, f);
+ }
+}
+
+final class FunctionalPrism<Whole, Part> implements Prism<Whole, Part> {
+ private final Function<Whole, Part> g;
+ private final Function<Part, Either<Part, Whole>> f;
+
+ public FunctionalPrism(Function<Whole, Part> g, Function<Part, Either<Part, Whole>> f) {
+ this.g = g;
+ this.f = f;
+ }
+
+ @Override
+ public Part build(Whole whole) {
+ return g.apply(whole);
+ }
+
+ @Override
+ public Either<Part, Whole> match(Part part) {
+ return f.apply(part);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/bjc/optics/PrismX.java b/src/main/java/bjc/optics/PrismX.java
index b4986cf..0f4a0e0 100644
--- a/src/main/java/bjc/optics/PrismX.java
+++ b/src/main/java/bjc/optics/PrismX.java
@@ -17,6 +17,73 @@
*/
package bjc.optics;
+import java.util.function.Function;
+
+import bjc.data.Either;
+
+/**
+ * Represents a Prism, which is a type of optic.
+ *
+ * TODO: Add better description
+ *
+ * @author bjcul
+ *
+ * @param <W1> The type of the first whole
+ * @param <W2> The type of the second whole
+ * @param <P1> The type of the first part
+ * @param <P2> The type of the second part
+ */
public interface PrismX<W1, W2, P1, P2> extends Optic<W1, W2, P1, P2> {
+ /**
+ * Match against this prism.
+ *
+ * @param part The part to match
+ * @return Either the matched value or the prism
+ */
+ Either<P2, W1> match(P1 part);
+ /**
+ * Build this prism from a given part.
+ *
+ * @param whole The whole to build from
+ * @return The part that is constructed
+ */
+ P2 build(W2 whole);
+
+ /**
+ * Create a prism from its component parts
+ *
+ * @param <W1> The type of the first whole
+ * @param <W2> The type of the second whole
+ * @param <P1> The type of the first part
+ * @param <P2> The type of the second part
+ *
+ * @param f The 'match' function for the prism
+ * @param g The 'build' function for the prism
+ *
+ * @return A prism built from the given parts
+ */
+ static <W1, W2, P1, P2> PrismX<W1, W2, P1, P2> of(Function<P1, Either<P2, W1>> f, Function<W2, P2> g) {
+ return new FunctionalPrismX<>(g, f);
+ }
}
+
+final class FunctionalPrismX<W1, W2, P1, P2> implements PrismX<W1, W2, P1, P2> {
+ private final Function<W2, P2> g;
+ private final Function<P1, Either<P2, W1>> f;
+
+ public FunctionalPrismX(Function<W2, P2> g, Function<P1, Either<P2, W1>> f) {
+ this.g = g;
+ this.f = f;
+ }
+
+ @Override
+ public P2 build(W2 whole) {
+ return g.apply(whole);
+ }
+
+ @Override
+ public Either<P2, W1> match(P1 part) {
+ return f.apply(part);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/bjc/optics/Prisms.java b/src/main/java/bjc/optics/Prisms.java
new file mode 100644
index 0000000..ae4a4ee
--- /dev/null
+++ b/src/main/java/bjc/optics/Prisms.java
@@ -0,0 +1,56 @@
+/*
+ * esodata - data structures 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.Optional;
+
+import bjc.data.Either;
+
+/**
+ * Various utilities for working with prisms
+ *
+ * @author bjcul
+ *
+ */
+public class Prisms {
+ /**
+ * Create a prism that reflects on the value in an optional
+ *
+ * @param <L> The type contained in the optional
+ * @param <R> An auxiliary type
+ *
+ * TODO: better notes for what R is for
+ *
+ * @return A prism that reflects on an optional
+ */
+ public static <L, R> PrismX<L, R, Optional<L>, Optional<R>> optional() {
+ return PrismX.of((opt) -> opt.isPresent() ? Either.right(opt.get()) : Either.left(Optional.empty()),
+ Optional::ofNullable);
+ }
+
+ /**
+ * Create a prism that reflects on whether a double corresponds to a given
+ * integer.
+ *
+ * @return A prism on the int view of a double.
+ */
+ public static Prism<Integer, Double> whole() {
+ return Prism.of((vl) -> Math.rint(vl) == vl ? Either.right(vl.intValue()) : Either.left(vl),
+ Integer::doubleValue);
+ }
+}
diff --git a/src/main/java/bjc/optics/Traversal.java b/src/main/java/bjc/optics/Traversal.java
new file mode 100644
index 0000000..a41f2b9
--- /dev/null
+++ b/src/main/java/bjc/optics/Traversal.java
@@ -0,0 +1,14 @@
+package bjc.optics;
+
+import bjc.typeclasses.BiContainer;
+
+/**
+ * A traversal
+ * @author bjcul
+ *
+ * @param <C> The container type
+ * @param <E> The element type
+ */
+public interface Traversal<C, E> extends TraversalX<C, C, E, E>, BiContainer<C, E, Traversal<C, E>> {
+ // TODO implement 'of'
+}
diff --git a/src/main/java/bjc/optics/TraversalX.java b/src/main/java/bjc/optics/TraversalX.java
new file mode 100644
index 0000000..54c1a0b
--- /dev/null
+++ b/src/main/java/bjc/optics/TraversalX.java
@@ -0,0 +1,54 @@
+/*
+ * esodata - data structures of varying use
+ * 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.Collection;
+
+/**
+ * Represents an optic allowing access to the elements of a given item
+ *
+ * @author bjcul
+ *
+ * @param <C1> The first collection type
+ * @param <C2> The second collection type
+ * @param <E1> The first element type
+ * @param <E2> The second element type
+ */
+public interface TraversalX<C1, C2, E1, E2> extends Optic<C1, C2, E1, E2> {
+ // Note: these should be fixed size collections of the same type, but that would
+ // be awkward to represent
+ /**
+ * Extract the contents of the collection
+ *
+ * @param whole The collection to get the contents of.
+ * @return The contents of the given collection.
+ */
+ Collection<E1> contents(C1 whole);
+
+ /**
+ * Replace the contents of the given collection
+ *
+ * @param whole The collection to replace the contents of
+ * @param bits The new contents of the collection
+ *
+ * @return A collection with the new contents
+ */
+ C2 fill(C1 whole, Collection<E2> bits);
+
+ // TODO implement 'of'
+}