diff options
Diffstat (limited to 'src/main/java/bjc/optics')
| -rw-r--r-- | src/main/java/bjc/optics/Adapter.java | 15 | ||||
| -rw-r--r-- | src/main/java/bjc/optics/AdapterX.java | 89 | ||||
| -rw-r--r-- | src/main/java/bjc/optics/Adapters.java | 47 | ||||
| -rw-r--r-- | src/main/java/bjc/optics/Lens.java | 38 | ||||
| -rw-r--r-- | src/main/java/bjc/optics/LensX.java | 1 | ||||
| -rw-r--r-- | src/main/java/bjc/optics/Lenses.java | 23 | ||||
| -rw-r--r-- | src/main/java/bjc/optics/Prism.java | 70 | ||||
| -rw-r--r-- | src/main/java/bjc/optics/PrismX.java | 67 | ||||
| -rw-r--r-- | src/main/java/bjc/optics/Prisms.java | 56 | ||||
| -rw-r--r-- | src/main/java/bjc/optics/Traversal.java | 14 | ||||
| -rw-r--r-- | src/main/java/bjc/optics/TraversalX.java | 54 |
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' +} |
