From 44be6e6cd7671dd243056107ffa6201504f7fbce Mon Sep 17 00:00:00 2001 From: Ben Culkin Date: Sun, 25 Jun 2023 15:50:38 -0400 Subject: Update a number of things --- src/main/java/bjc/optics/Adapter.java | 15 ++++++ src/main/java/bjc/optics/AdapterX.java | 89 ++++++++++++++++++++++++++++++++ src/main/java/bjc/optics/Adapters.java | 47 +++++++++++++++++ src/main/java/bjc/optics/Lens.java | 38 +++++++++++--- src/main/java/bjc/optics/LensX.java | 1 - src/main/java/bjc/optics/Lenses.java | 23 +++++++-- src/main/java/bjc/optics/Prism.java | 70 +++++++++++++++++++++++++ src/main/java/bjc/optics/PrismX.java | 67 ++++++++++++++++++++++++ src/main/java/bjc/optics/Prisms.java | 56 ++++++++++++++++++++ src/main/java/bjc/optics/Traversal.java | 14 +++++ src/main/java/bjc/optics/TraversalX.java | 54 +++++++++++++++++++ 11 files changed, 462 insertions(+), 12 deletions(-) create mode 100644 src/main/java/bjc/optics/Adapter.java create mode 100644 src/main/java/bjc/optics/AdapterX.java create mode 100644 src/main/java/bjc/optics/Adapters.java create mode 100644 src/main/java/bjc/optics/Prism.java create mode 100644 src/main/java/bjc/optics/Prisms.java create mode 100644 src/main/java/bjc/optics/Traversal.java create mode 100644 src/main/java/bjc/optics/TraversalX.java (limited to 'src/main/java/bjc/optics') 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 The source type + * @param The destination type + */ +public interface Adapter + extends AdapterX, BiContainer> { + // 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 . + */ +package bjc.optics; + +import java.util.function.Function; + +/** + * An adapter that maps between two sets of types + * + * @author bjcul + * + * @param The type of the first source + * @param The type of the second source + * @param The type of the first destination + * @param The type of the second destination + */ +public interface AdapterX extends Optic { + + /** + * 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 The type of the first source + * @param The type of the second source + * @param The type of the first destination + * @param 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 AdapterX of(Function f, Function g) { + return new FunctionalAdapterX<>(f, g); + } +} + +final class FunctionalAdapterX implements AdapterX { + private final Function f; + private final Function g; + + public FunctionalAdapterX(Function f, Function 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 . + */ +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 The first left type + * @param The second left type + * @param The first middle type + * @param The second middle type + * @param The first right type + * @param The second right type + * + * @return An adapter between triples and left-nested pairs + */ + public static AdapterX, Triple, Pair, C1>, + Pair, 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 The item this lens can focus on - * @param The field this lens focuses on + * @param The field this lens focuses on */ -public interface Lens extends LensX, BiContainer> { +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 + * @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. * @@ -53,16 +55,36 @@ public interface Lens extends LensX, BiCo 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. + * @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); } + + /** + * 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 Lens compose(Lens other) { + LensX 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) 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 LensX, Holder, 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 LensX, Pair, 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 Lens> both(Lens lhs, + // Lens rhs) { + // } + /** * Creates a lens which focuses on a piece of internal state. * @@ -107,7 +113,7 @@ public class Lenses { 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. * @@ -121,6 +127,17 @@ public class Lenses { Holder 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 sign() { + LensX lensX = immutable((num) -> num >= 0, (num, sgn) -> sgn ? Math.abs(num) : -Math.abs(num)); + return (Lens) lensX; + } } final class FunctionalLensX implements LensX { 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 . + */ +package bjc.optics; + +import java.util.function.Function; + +import bjc.data.Either; +import bjc.typeclasses.BiContainer; + +/** + * A type-invariant prism + * + * @author bjcul + * + * @param The type of the whole + * @param The type of the part + */ +public interface Prism + extends PrismX, BiContainer> { + + /** + * Create a type-invariant prism from its component parts. + * + * @param The type of the whole + * @param 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 Prism of(Function> f, Function g) { + return new FunctionalPrism<>(g, f); + } +} + +final class FunctionalPrism implements Prism { + private final Function g; + private final Function> f; + + public FunctionalPrism(Function g, Function> f) { + this.g = g; + this.f = f; + } + + @Override + public Part build(Whole whole) { + return g.apply(whole); + } + + @Override + public Either 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 The type of the first whole + * @param The type of the second whole + * @param The type of the first part + * @param The type of the second part + */ public interface PrismX extends Optic { + /** + * Match against this prism. + * + * @param part The part to match + * @return Either the matched value or the prism + */ + Either 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 The type of the first whole + * @param The type of the second whole + * @param The type of the first part + * @param 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 PrismX of(Function> f, Function g) { + return new FunctionalPrismX<>(g, f); + } } + +final class FunctionalPrismX implements PrismX { + private final Function g; + private final Function> f; + + public FunctionalPrismX(Function g, Function> f) { + this.g = g; + this.f = f; + } + + @Override + public P2 build(W2 whole) { + return g.apply(whole); + } + + @Override + public Either 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 . + */ +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 The type contained in the optional + * @param An auxiliary type + * + * TODO: better notes for what R is for + * + * @return A prism that reflects on an optional + */ + public static PrismX, Optional> 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 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 The container type + * @param The element type + */ +public interface Traversal extends TraversalX, BiContainer> { + // 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 . + */ +package bjc.optics; + +import java.util.Collection; + +/** + * Represents an optic allowing access to the elements of a given item + * + * @author bjcul + * + * @param The first collection type + * @param The second collection type + * @param The first element type + * @param The second element type + */ +public interface TraversalX extends Optic { + // 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 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 bits); + + // TODO implement 'of' +} -- cgit v1.2.3