From eba9e672cfbc577afa38f2a7e9b70d7069f89218 Mon Sep 17 00:00:00 2001
From: Alexander Bandukwala <7h3kk1d@gmail.com>
Date: Mon, 1 May 2023 16:41:46 -0500
Subject: [PATCH 1/5] Change language version to 17

---
 pom.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/pom.xml b/pom.xml
index bae03b1e..07e73502 100644
--- a/pom.xml
+++ b/pom.xml
@@ -91,8 +91,8 @@
                 <artifactId>maven-compiler-plugin</artifactId>
                 <version>${maven-compiler-plugin.version}</version>
                 <configuration>
-                    <source>1.8</source>
-                    <target>1.8</target>
+                    <source>17</source>
+                    <target>17</target>
                     <compilerArgs>
                         <compilerArg>-Xlint:all</compilerArg>
                         <compilerArg>-Werror</compilerArg>

From 59fa9129148ee0061800c5f2f810e9ba06255582 Mon Sep 17 00:00:00 2001
From: Alexander Bandukwala <7h3kk1d@gmail.com>
Date: Mon, 1 May 2023 16:47:06 -0500
Subject: [PATCH 2/5] Convert some classes to records

---
 README.md                                     | 23 +++++----------
 .../lambda/functor/builtin/Exchange.java      | 12 +++-----
 .../lambda/functor/builtin/Market.java        | 21 +++++++-------
 .../testsupport/assertion/PrismAssert.java    | 28 ++++++-------------
 4 files changed, 30 insertions(+), 54 deletions(-)

diff --git a/README.md b/README.md
index 7d84c629..2e6aa98d 100644
--- a/README.md
+++ b/README.md
@@ -672,24 +672,15 @@ There are three functions that lambda provides that interface directly with lens
 Lenses can be easily created. Consider the following `Person` class:
 
 ```Java
-public final class Person {
-    private final int age;
+public record Person(int age) {
 
-    public Person(int age) {
-        this.age = age;
-    }
-
-    public int getAge() {
-        return age;
-    }
+   public Person setAge(int age) {
+      return new Person(age);
+   }
 
-    public Person setAge(int age) {
-        return new Person(age);
-    }
-
-    public Person setAge(LocalDate dob) {
-        return setAge((int) YEARS.between(dob, LocalDate.now()));
-    }
+   public Person setAge(LocalDate dob) {
+      return setAge((int) YEARS.between(dob, LocalDate.now()));
+   }
 }
 ```
 
diff --git a/src/main/java/com/jnape/palatable/lambda/functor/builtin/Exchange.java b/src/main/java/com/jnape/palatable/lambda/functor/builtin/Exchange.java
index f0405036..bfd861e1 100644
--- a/src/main/java/com/jnape/palatable/lambda/functor/builtin/Exchange.java
+++ b/src/main/java/com/jnape/palatable/lambda/functor/builtin/Exchange.java
@@ -12,20 +12,15 @@
  * @param <S> the larger viewing value of an {@link Iso}
  * @param <T> the larger viewed value of an {@link Iso}
  */
-public final class Exchange<A, B, S, T> implements Profunctor<S, T, Exchange<A, B, ?, ?>> {
-    private final Fn1<? super S, ? extends A> sa;
-    private final Fn1<? super B, ? extends T> bt;
-
-    public Exchange(Fn1<? super S, ? extends A> sa, Fn1<? super B, ? extends T> bt) {
-        this.sa = sa;
-        this.bt = bt;
-    }
+public record Exchange<A, B, S, T>(Fn1<? super S, ? extends A> sa,
+                                   Fn1<? super B, ? extends T> bt) implements Profunctor<S, T, Exchange<A, B, ?, ?>> {
 
     /**
      * Extract the mapping <code>S -&gt; A</code>.
      *
      * @return an <code>{@link Fn1}&lt;S, A&gt;</code>
      */
+    @Override
     public Fn1<? super S, ? extends A> sa() {
         return sa;
     }
@@ -35,6 +30,7 @@ public Exchange(Fn1<? super S, ? extends A> sa, Fn1<? super B, ? extends T> bt)
      *
      * @return an <code>{@link Fn1}&lt;B, T&gt;</code>
      */
+    @Override
     public Fn1<? super B, ? extends T> bt() {
         return bt;
     }
diff --git a/src/main/java/com/jnape/palatable/lambda/functor/builtin/Market.java b/src/main/java/com/jnape/palatable/lambda/functor/builtin/Market.java
index 3c8ce757..f5cb2937 100644
--- a/src/main/java/com/jnape/palatable/lambda/functor/builtin/Market.java
+++ b/src/main/java/com/jnape/palatable/lambda/functor/builtin/Market.java
@@ -28,13 +28,10 @@
  * @param <S> the input that might fail to map to its output
  * @param <T> the guaranteed output
  */
-public final class Market<A, B, S, T> implements
+public record Market<A, B, S, T>(Fn1<? super B, ? extends T> bt, Fn1<? super S, ? extends Either<T, A>> sta) implements
         MonadRec<T, Market<A, B, S, ?>>,
         Cocartesian<S, T, Market<A, B, ?, ?>> {
 
-    private final Fn1<? super B, ? extends T>            bt;
-    private final Fn1<? super S, ? extends Either<T, A>> sta;
-
     public Market(Fn1<? super B, ? extends T> bt, Fn1<? super S, ? extends Either<T, A>> sta) {
         this.bt = fn1(bt);
         this.sta = fn1(sta);
@@ -45,6 +42,7 @@ public Market(Fn1<? super B, ? extends T> bt, Fn1<? super S, ? extends Either<T,
      *
      * @return a <code>{@link Fn1}&lt;B, T&gt;</code>
      */
+    @Override
     public Fn1<? super B, ? extends T> bt() {
         return bt;
     }
@@ -54,6 +52,7 @@ public Market(Fn1<? super B, ? extends T> bt, Fn1<? super S, ? extends Either<T,
      *
      * @return a <code>{@link Fn1}&lt;S, {@link Either}&lt;T, A&gt;&gt;</code>
      */
+    @Override
     public Fn1<? super S, ? extends Either<T, A>> sta() {
         return sta;
     }
@@ -72,9 +71,9 @@ public <U> Market<A, B, S, U> pure(U u) {
     @Override
     public <U> Market<A, B, S, U> flatMap(Fn1<? super T, ? extends Monad<U, Market<A, B, S, ?>>> f) {
         return new Market<>(b -> f.apply(bt().apply(b)).<Market<A, B, S, U>>coerce().bt().apply(b),
-                            s -> sta().apply(s).invert()
-                                    .flatMap(t -> f.apply(t).<Market<A, B, S, U>>coerce().sta()
-                                            .apply(s).invert()).invert());
+                s -> sta().apply(s).invert()
+                        .flatMap(t -> f.apply(t).<Market<A, B, S, U>>coerce().sta()
+                                .apply(s).invert()).invert());
     }
 
     /**
@@ -89,7 +88,7 @@ public <U> Market<A, B, S, U> trampolineM(
                         trampoline(t -> fn.apply(t).<Market<A, B, S, RecursiveResult<T, U>>>coerce()
                                 .sta.apply(s)
                                 .match(tOrU -> tOrU.match(RecursiveResult::recurse, u -> terminate(left(u))),
-                                       a -> terminate(right(a)))),
+                                        a -> terminate(right(a)))),
                         Either::right)));
         return new Market<>(bu, sua);
     }
@@ -101,7 +100,7 @@ public <U> Market<A, B, S, U> trampolineM(
     public <U> Market<A, B, S, U> zip(Applicative<Fn1<? super T, ? extends U>, Market<A, B, S, ?>> appFn) {
         Market<A, B, S, Fn1<? super T, ? extends U>> marketF = appFn.coerce();
         return new Market<>(b -> marketF.bt().apply(b).apply(bt().apply(b)),
-                            s -> sta().apply(s).invert().zip(marketF.sta().apply(s).invert()).invert());
+                s -> sta().apply(s).invert().zip(marketF.sta().apply(s).invert()).invert());
     }
 
     /**
@@ -118,8 +117,8 @@ public <U> Market<A, B, S, U> fmap(Fn1<? super T, ? extends U> fn) {
     @Override
     public <C> Market<A, B, Choice2<C, S>, Choice2<C, T>> cocartesian() {
         return new Market<>(bt.fmap(Choice2::b),
-                            cs -> cs.fmap(sta).match(c -> left(a(c)),
-                                                     tOrA -> tOrA.match(t -> left(b(t)), Either::right)));
+                cs -> cs.fmap(sta).match(c -> left(a(c)),
+                        tOrA -> tOrA.match(t -> left(b(t)), Either::right)));
     }
 
     /**
diff --git a/src/test/java/testsupport/assertion/PrismAssert.java b/src/test/java/testsupport/assertion/PrismAssert.java
index 443130fb..f10e30e8 100644
--- a/src/test/java/testsupport/assertion/PrismAssert.java
+++ b/src/test/java/testsupport/assertion/PrismAssert.java
@@ -59,29 +59,19 @@ private static <S, A, X> Maybe<String> falsify(String label, Fn2<S, A, X> l, Fn2
                 .apply(cases);
     }
 
-    private static final class PrismResult<S> {
-        private final Maybe<S> maybeS;
-
-        private PrismResult(Maybe<S> maybeS) {
-            this.maybeS = maybeS;
-        }
+    private record PrismResult<S>(Maybe<S> maybeS) {
 
         @Override
-        public boolean equals(Object other) {
-            if (other instanceof PrismResult) {
-                return maybeS.zip(((PrismResult<?>) other).maybeS.fmap(fn2(Objects::equals))).orElse(true);
+            public boolean equals(Object other) {
+                if (other instanceof PrismResult) {
+                    return maybeS.zip(((PrismResult<?>) other).maybeS.fmap(fn2(Objects::equals))).orElse(true);
+                }
+                return false;
             }
-            return false;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(maybeS);
-        }
 
         @Override
-        public String toString() {
-            return maybeS.toString();
+            public String toString() {
+                return maybeS.toString();
+            }
         }
-    }
 }

From aba344aab4e79251548a52bb6070b6bb4a4a3571 Mon Sep 17 00:00:00 2001
From: Alexander Bandukwala <7h3kk1d@gmail.com>
Date: Mon, 1 May 2023 16:50:15 -0500
Subject: [PATCH 3/5] Use pattern variables

---
 .../java/com/jnape/palatable/lambda/adt/hlist/HList.java    | 3 +--
 src/main/java/com/jnape/palatable/lambda/adt/hmap/HMap.java | 3 +--
 .../com/jnape/palatable/lambda/functor/builtin/Lazy.java    | 3 +--
 .../lambda/internal/iteration/DroppingIterable.java         | 3 +--
 .../lambda/internal/iteration/FilteringIterable.java        | 3 +--
 .../lambda/internal/iteration/MappingIterable.java          | 3 +--
 .../internal/iteration/PredicatedDroppingIterable.java      | 3 +--
 .../lambda/internal/iteration/PredicatedTakingIterable.java | 3 +--
 .../lambda/internal/iteration/RateLimitingIterable.java     | 3 +--
 .../lambda/internal/iteration/ReversingIterable.java        | 3 +--
 .../palatable/lambda/internal/iteration/SnocIterable.java   | 3 +--
 .../palatable/lambda/internal/iteration/TakingIterable.java | 3 +--
 src/main/java/com/jnape/palatable/lambda/io/IO.java         | 6 ++----
 13 files changed, 14 insertions(+), 28 deletions(-)

diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/HList.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/HList.java
index 705e43ba..0dc877fc 100644
--- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/HList.java
+++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/HList.java
@@ -263,8 +263,7 @@ public Tail tail() {
 
         @Override
         public final boolean equals(Object other) {
-            if (other instanceof HCons) {
-                HCons<?, ?> that = (HCons<?, ?>) other;
+            if (other instanceof HCons<?, ?> that) {
                 return this.head.equals(that.head)
                         && this.tail.equals(that.tail);
             }
diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hmap/HMap.java b/src/main/java/com/jnape/palatable/lambda/adt/hmap/HMap.java
index 62c922ec..db106f41 100644
--- a/src/main/java/com/jnape/palatable/lambda/adt/hmap/HMap.java
+++ b/src/main/java/com/jnape/palatable/lambda/adt/hmap/HMap.java
@@ -162,8 +162,7 @@ public Collection<Object> values() {
 
     @Override
     public boolean equals(Object other) {
-        if (other instanceof HMap) {
-            HMap that = (HMap) other;
+        if (other instanceof HMap that) {
             return Objects.equals(this.table, that.table);
         }
         return false;
diff --git a/src/main/java/com/jnape/palatable/lambda/functor/builtin/Lazy.java b/src/main/java/com/jnape/palatable/lambda/functor/builtin/Lazy.java
index 83ec4771..6d941173 100644
--- a/src/main/java/com/jnape/palatable/lambda/functor/builtin/Lazy.java
+++ b/src/main/java/com/jnape/palatable/lambda/functor/builtin/Lazy.java
@@ -182,8 +182,7 @@ public A value() {
                     tuple((Lazy<Object>) this, new LinkedList<>());
             @SuppressWarnings("unchecked")
             A a = (A) trampoline(into((source, flatMaps) -> {
-                if (source instanceof Compose<?>) {
-                    Compose<?> nested = (Compose<?>) source;
+                if (source instanceof Compose<?> nested) {
                     flatMaps.push(nested.flatMap);
                     return recurse(tuple(nested.source, flatMaps));
                 }
diff --git a/src/main/java/com/jnape/palatable/lambda/internal/iteration/DroppingIterable.java b/src/main/java/com/jnape/palatable/lambda/internal/iteration/DroppingIterable.java
index 1d9534b1..88a1719e 100644
--- a/src/main/java/com/jnape/palatable/lambda/internal/iteration/DroppingIterable.java
+++ b/src/main/java/com/jnape/palatable/lambda/internal/iteration/DroppingIterable.java
@@ -7,8 +7,7 @@ public final class DroppingIterable<A> implements Iterable<A> {
     private final Iterable<A> as;
 
     public DroppingIterable(int n, Iterable<A> as) {
-        while (as instanceof DroppingIterable) {
-            DroppingIterable<A> nested = (DroppingIterable<A>) as;
+        while (as instanceof DroppingIterable<A> nested) {
             as = nested.as;
             n += nested.n;
         }
diff --git a/src/main/java/com/jnape/palatable/lambda/internal/iteration/FilteringIterable.java b/src/main/java/com/jnape/palatable/lambda/internal/iteration/FilteringIterable.java
index 7accde2f..75c4879a 100644
--- a/src/main/java/com/jnape/palatable/lambda/internal/iteration/FilteringIterable.java
+++ b/src/main/java/com/jnape/palatable/lambda/internal/iteration/FilteringIterable.java
@@ -15,8 +15,7 @@ public final class FilteringIterable<A> implements Iterable<A> {
 
     public FilteringIterable(Fn1<? super A, ? extends Boolean> predicate, Iterable<A> as) {
         List<Fn1<? super A, ? extends Boolean>> predicates = new ArrayList<>(singletonList(predicate));
-        while (as instanceof FilteringIterable) {
-            FilteringIterable<A> nested = (FilteringIterable<A>) as;
+        while (as instanceof FilteringIterable<A> nested) {
             predicates.addAll(0, nested.predicates);
             as = nested.as;
         }
diff --git a/src/main/java/com/jnape/palatable/lambda/internal/iteration/MappingIterable.java b/src/main/java/com/jnape/palatable/lambda/internal/iteration/MappingIterable.java
index 3d2f1ba6..548fc795 100644
--- a/src/main/java/com/jnape/palatable/lambda/internal/iteration/MappingIterable.java
+++ b/src/main/java/com/jnape/palatable/lambda/internal/iteration/MappingIterable.java
@@ -16,8 +16,7 @@ public final class MappingIterable<A, B> implements Iterable<B> {
     @SuppressWarnings("unchecked")
     public MappingIterable(Fn1<? super A, ? extends B> fn, Iterable<A> as) {
         List<Fn1<?, ?>> mappers = new ArrayList<>(singletonList(fn));
-        while (as instanceof MappingIterable<?, ?>) {
-            MappingIterable<?, ?> nested = (MappingIterable<?, ?>) as;
+        while (as instanceof MappingIterable<?, ?> nested) {
             as = (Iterable<A>) nested.as;
             mappers.addAll(0, nested.mappers);
         }
diff --git a/src/main/java/com/jnape/palatable/lambda/internal/iteration/PredicatedDroppingIterable.java b/src/main/java/com/jnape/palatable/lambda/internal/iteration/PredicatedDroppingIterable.java
index ea7b68bc..e96310b1 100644
--- a/src/main/java/com/jnape/palatable/lambda/internal/iteration/PredicatedDroppingIterable.java
+++ b/src/main/java/com/jnape/palatable/lambda/internal/iteration/PredicatedDroppingIterable.java
@@ -11,8 +11,7 @@ public final class PredicatedDroppingIterable<A> implements Iterable<A> {
 
     public PredicatedDroppingIterable(Fn1<? super A, ? extends Boolean> predicate, Iterable<A> as) {
         ImmutableQueue<Fn1<? super A, ? extends Boolean>> predicates = ImmutableQueue.singleton(predicate);
-        while (as instanceof PredicatedDroppingIterable) {
-            PredicatedDroppingIterable<A> nested = (PredicatedDroppingIterable<A>) as;
+        while (as instanceof PredicatedDroppingIterable<A> nested) {
             as = nested.as;
             predicates = nested.predicates.concat(predicates);
         }
diff --git a/src/main/java/com/jnape/palatable/lambda/internal/iteration/PredicatedTakingIterable.java b/src/main/java/com/jnape/palatable/lambda/internal/iteration/PredicatedTakingIterable.java
index de9c7682..92d5a5a5 100644
--- a/src/main/java/com/jnape/palatable/lambda/internal/iteration/PredicatedTakingIterable.java
+++ b/src/main/java/com/jnape/palatable/lambda/internal/iteration/PredicatedTakingIterable.java
@@ -15,8 +15,7 @@ public final class PredicatedTakingIterable<A> implements Iterable<A> {
 
     public PredicatedTakingIterable(Fn1<? super A, ? extends Boolean> predicate, Iterable<A> as) {
         List<Fn1<? super A, ? extends Boolean>> predicates = new ArrayList<>(singletonList(predicate));
-        while (as instanceof PredicatedTakingIterable) {
-            PredicatedTakingIterable<A> nested = (PredicatedTakingIterable<A>) as;
+        while (as instanceof PredicatedTakingIterable<A> nested) {
             predicates.addAll(0, nested.predicates);
             as = nested.as;
         }
diff --git a/src/main/java/com/jnape/palatable/lambda/internal/iteration/RateLimitingIterable.java b/src/main/java/com/jnape/palatable/lambda/internal/iteration/RateLimitingIterable.java
index 3a7c856d..fe5240dd 100644
--- a/src/main/java/com/jnape/palatable/lambda/internal/iteration/RateLimitingIterable.java
+++ b/src/main/java/com/jnape/palatable/lambda/internal/iteration/RateLimitingIterable.java
@@ -15,8 +15,7 @@ public final class RateLimitingIterable<A> implements Iterable<A> {
 
     public RateLimitingIterable(Iterable<A> as, Set<Tuple3<Long, Duration, Fn0<Instant>>> rateLimits) {
         Set<Tuple3<Long, Duration, Fn0<Instant>>> combinedRateLimits = new HashSet<>(rateLimits);
-        if (as instanceof RateLimitingIterable) {
-            RateLimitingIterable<A> inner = (RateLimitingIterable<A>) as;
+        if (as instanceof RateLimitingIterable<A> inner) {
             combinedRateLimits.addAll(inner.rateLimits);
             as = inner.as;
         }
diff --git a/src/main/java/com/jnape/palatable/lambda/internal/iteration/ReversingIterable.java b/src/main/java/com/jnape/palatable/lambda/internal/iteration/ReversingIterable.java
index 29aff81d..46afde55 100644
--- a/src/main/java/com/jnape/palatable/lambda/internal/iteration/ReversingIterable.java
+++ b/src/main/java/com/jnape/palatable/lambda/internal/iteration/ReversingIterable.java
@@ -8,8 +8,7 @@ public final class ReversingIterable<A> implements Iterable<A> {
 
     public ReversingIterable(Iterable<A> as) {
         boolean reverse = true;
-        while (as instanceof ReversingIterable) {
-            ReversingIterable<A> nested = (ReversingIterable<A>) as;
+        while (as instanceof ReversingIterable<A> nested) {
             as = nested.as;
             reverse = !nested.reverse;
         }
diff --git a/src/main/java/com/jnape/palatable/lambda/internal/iteration/SnocIterable.java b/src/main/java/com/jnape/palatable/lambda/internal/iteration/SnocIterable.java
index 9a011820..b43c994b 100644
--- a/src/main/java/com/jnape/palatable/lambda/internal/iteration/SnocIterable.java
+++ b/src/main/java/com/jnape/palatable/lambda/internal/iteration/SnocIterable.java
@@ -12,8 +12,7 @@ public final class SnocIterable<A> implements Iterable<A> {
 
     public SnocIterable(A a, Iterable<A> as) {
         Iterable<A> snocs = cons(a, Collections::emptyIterator);
-        while (as instanceof SnocIterable) {
-            SnocIterable<A> nested = ((SnocIterable<A>) as);
+        while (as instanceof SnocIterable<A> nested) {
             as = nested.as;
             snocs = concat(nested.snocs, snocs);
         }
diff --git a/src/main/java/com/jnape/palatable/lambda/internal/iteration/TakingIterable.java b/src/main/java/com/jnape/palatable/lambda/internal/iteration/TakingIterable.java
index f228b596..3d153dd5 100644
--- a/src/main/java/com/jnape/palatable/lambda/internal/iteration/TakingIterable.java
+++ b/src/main/java/com/jnape/palatable/lambda/internal/iteration/TakingIterable.java
@@ -9,8 +9,7 @@ public final class TakingIterable<A> implements Iterable<A> {
     private final Iterable<A> as;
 
     public TakingIterable(int n, Iterable<A> as) {
-        while (as instanceof TakingIterable) {
-            TakingIterable<A> nested = (TakingIterable<A>) as;
+        while (as instanceof TakingIterable<A> nested) {
             n = min(n, nested.n);
             as = nested.as;
         }
diff --git a/src/main/java/com/jnape/palatable/lambda/io/IO.java b/src/main/java/com/jnape/palatable/lambda/io/IO.java
index 6af4dc1b..65214986 100644
--- a/src/main/java/com/jnape/palatable/lambda/io/IO.java
+++ b/src/main/java/com/jnape/palatable/lambda/io/IO.java
@@ -430,8 +430,7 @@ private Compose(IO<?> source, Choice2<IO<?>, Fn1<Object, IO<?>>> composition) {
         public A unsafePerformIO() {
             Lazy<Object> lazyA = LazyRec.<IO<?>, Object>lazyRec(
                     (f, io) -> {
-                        if (io instanceof IO.Compose<?>) {
-                            Compose<?>   compose = (Compose<?>) io;
+                        if (io instanceof Compose<?> compose) {
                             Lazy<Object> head    = f.apply(compose.source);
                             return compose.composition
                                     .match(zip -> head.flatMap(x -> f.apply(zip)
@@ -450,8 +449,7 @@ public A unsafePerformIO() {
         public CompletableFuture<A> unsafePerformAsyncIO(Executor executor) {
             Lazy<CompletableFuture<Object>> lazyFuture = LazyRec.<IO<?>, CompletableFuture<Object>>lazyRec(
                     (f, io) -> {
-                        if (io instanceof IO.Compose<?>) {
-                            Compose<?>                      compose = (Compose<?>) io;
+                        if (io instanceof Compose<?> compose) {
                             Lazy<CompletableFuture<Object>> head    = f.apply(compose.source);
                             return compose.composition
                                     .match(zip -> head.flatMap(futureX -> f.apply(zip)

From 956b1185fbfe3f3d46a72d516f9f7340294b84bd Mon Sep 17 00:00:00 2001
From: Alexander Bandukwala <7h3kk1d@gmail.com>
Date: Mon, 1 May 2023 16:50:57 -0500
Subject: [PATCH 4/5] Use collection factory calls

---
 .../palatable/lambda/optics/lenses/MapLensTest.java  | 12 ++----------
 1 file changed, 2 insertions(+), 10 deletions(-)

diff --git a/src/test/java/com/jnape/palatable/lambda/optics/lenses/MapLensTest.java b/src/test/java/com/jnape/palatable/lambda/optics/lenses/MapLensTest.java
index 5a97e5b4..39c15b3e 100644
--- a/src/test/java/com/jnape/palatable/lambda/optics/lenses/MapLensTest.java
+++ b/src/test/java/com/jnape/palatable/lambda/optics/lenses/MapLensTest.java
@@ -176,17 +176,9 @@ public void mappingValuesWithIsoRetainsMapStructureWithMappedValues() {
         assertLensLawfulness(mappingValues(iso(Integer::parseInt, Object::toString)),
                              asList(emptyMap(),
                                     singletonMap("foo", "1"),
-                                    unmodifiableMap(new HashMap<String, String>() {{
-                                        put("foo", "1");
-                                        put("bar", "2");
-                                        put("baz", "3");
-                                    }})),
+                                     Map.of("foo", "1", "bar", "2", "baz", "3")),
                              asList(emptyMap(),
                                     singletonMap("foo", 1),
-                                    unmodifiableMap(new HashMap<String, Integer>() {{
-                                        put("foo", 1);
-                                        put("bar", 2);
-                                        put("baz", 3);
-                                    }})));
+                                     Map.of("foo", 1, "bar", 2, "baz", 3)));
     }
 }
\ No newline at end of file

From 83c8aae92868b2013ab6f7f12d1343ce22c2ec10 Mon Sep 17 00:00:00 2001
From: Alexander Bandukwala <7h3kk1d@gmail.com>
Date: Mon, 1 May 2023 17:12:30 -0500
Subject: [PATCH 5/5] Try making maybe a sealed interface

---
 .../com/jnape/palatable/lambda/adt/Maybe.java | 116 +++++++-----------
 1 file changed, 41 insertions(+), 75 deletions(-)

diff --git a/src/main/java/com/jnape/palatable/lambda/adt/Maybe.java b/src/main/java/com/jnape/palatable/lambda/adt/Maybe.java
index dc559598..acb46311 100644
--- a/src/main/java/com/jnape/palatable/lambda/adt/Maybe.java
+++ b/src/main/java/com/jnape/palatable/lambda/adt/Maybe.java
@@ -18,7 +18,6 @@
 import com.jnape.palatable.lambda.monad.MonadRec;
 import com.jnape.palatable.lambda.traversable.Traversable;
 
-import java.util.Objects;
 import java.util.Optional;
 
 import static com.jnape.palatable.lambda.adt.Either.left;
@@ -38,14 +37,12 @@
  * @param <A> the optional parameter type
  * @see Optional
  */
-public abstract class Maybe<A> implements
+public sealed interface Maybe<A> extends
         CoProduct2<Unit, A, Maybe<A>>,
         MonadError<Unit, A, Maybe<?>>,
         MonadRec<A, Maybe<?>>,
-        Traversable<A, Maybe<?>> {
+        Traversable<A, Maybe<?>> permits Just, Nothing {
 
-    private Maybe() {
-    }
 
     /**
      * If the value is present, return it; otherwise, return the value supplied by <code>otherSupplier</code>.
@@ -53,7 +50,7 @@ private Maybe() {
      * @param otherFn0 the supplier for the other value
      * @return this value, or the supplied other value
      */
-    public final A orElseGet(Fn0<A> otherFn0) {
+    default A orElseGet(Fn0<A> otherFn0) {
         return match(__ -> otherFn0.apply(), id());
     }
 
@@ -63,7 +60,7 @@ public final A orElseGet(Fn0<A> otherFn0) {
      * @param other the other value
      * @return this value, or the other value
      */
-    public final A orElse(A other) {
+    default A orElse(A other) {
         return orElseGet(() -> other);
     }
 
@@ -76,7 +73,7 @@ public final A orElse(A other) {
      * @return the value, if present
      * @throws E the throwable, if the value is absent
      */
-    public final <E extends Throwable> A orElseThrow(Fn0<? extends E> throwableSupplier) throws E {
+    default <E extends Throwable> A orElseThrow(Fn0<? extends E> throwableSupplier) throws E {
         return orElseGet(fn0(() -> {
             throw throwableSupplier.apply();
         }));
@@ -89,7 +86,7 @@ public final <E extends Throwable> A orElseThrow(Fn0<? extends E> throwableSuppl
      * @param predicate the predicate to apply to the possibly absent value
      * @return maybe the present value that satisfied the predicate
      */
-    public final Maybe<A> filter(Fn1<? super A, ? extends Boolean> predicate) {
+    default Maybe<A> filter(Fn1<? super A, ? extends Boolean> predicate) {
         return flatMap(a -> predicate.apply(a) ? just(a) : nothing());
     }
 
@@ -97,7 +94,7 @@ public final Maybe<A> filter(Fn1<? super A, ? extends Boolean> predicate) {
      * {@inheritDoc}
      */
     @Override
-    public Maybe<A> throwError(Unit unit) {
+    default Maybe<A> throwError(Unit unit) {
         return nothing();
     }
 
@@ -105,7 +102,7 @@ public Maybe<A> throwError(Unit unit) {
      * {@inheritDoc}
      */
     @Override
-    public Maybe<A> catchError(Fn1<? super Unit, ? extends Monad<A, Maybe<?>>> recoveryFn) {
+    default Maybe<A> catchError(Fn1<? super Unit, ? extends Monad<A, Maybe<?>>> recoveryFn) {
         return match(recoveryFn, Maybe::just).coerce();
     }
 
@@ -117,7 +114,7 @@ public Maybe<A> catchError(Fn1<? super Unit, ? extends Monad<A, Maybe<?>>> recov
      * @param lFn0 the supplier for the left value
      * @return this value wrapped in an Either.right, or an Either.left around the result of lSupplier
      */
-    public final <L> Either<L, A> toEither(Fn0<L> lFn0) {
+    default <L> Either<L, A> toEither(Fn0<L> lFn0) {
         return fmap(Either::<L, A>right).orElseGet(() -> left(lFn0.apply()));
     }
 
@@ -126,7 +123,7 @@ public final <L> Either<L, A> toEither(Fn0<L> lFn0) {
      *
      * @return the Optional
      */
-    public final Optional<A> toOptional() {
+    default Optional<A> toOptional() {
         return fmap(Optional::of).orElseGet(Optional::empty);
     }
 
@@ -138,7 +135,7 @@ public final Optional<A> toOptional() {
      * @return Just b
      */
     @Override
-    public final <B> Maybe<B> pure(B b) {
+    default <B> Maybe<B> pure(B b) {
         return just(b);
     }
 
@@ -149,7 +146,7 @@ public final <B> Maybe<B> pure(B b) {
      * {@link Maybe#nothing}.
      */
     @Override
-    public final <B> Maybe<B> fmap(Fn1<? super A, ? extends B> fn) {
+    default <B> Maybe<B> fmap(Fn1<? super A, ? extends B> fn) {
         return MonadError.super.<B>fmap(fn).coerce();
     }
 
@@ -157,7 +154,7 @@ public final <B> Maybe<B> fmap(Fn1<? super A, ? extends B> fn) {
      * {@inheritDoc}
      */
     @Override
-    public final <B> Maybe<B> zip(Applicative<Fn1<? super A, ? extends B>, Maybe<?>> appFn) {
+    default <B> Maybe<B> zip(Applicative<Fn1<? super A, ? extends B>, Maybe<?>> appFn) {
         return MonadError.super.zip(appFn).coerce();
     }
 
@@ -169,7 +166,7 @@ public final <B> Maybe<B> zip(Applicative<Fn1<? super A, ? extends B>, Maybe<?>>
      * @return the zipped {@link Maybe}
      */
     @Override
-    public <B> Lazy<Maybe<B>> lazyZip(Lazy<? extends Applicative<Fn1<? super A, ? extends B>, Maybe<?>>> lazyAppFn) {
+    default <B> Lazy<Maybe<B>> lazyZip(Lazy<? extends Applicative<Fn1<? super A, ? extends B>, Maybe<?>>> lazyAppFn) {
         return match(constantly(lazy(nothing())),
                      a -> lazyAppFn.fmap(maybeF -> maybeF.<B>fmap(f -> f.apply(a)).coerce()));
     }
@@ -178,7 +175,7 @@ public <B> Lazy<Maybe<B>> lazyZip(Lazy<? extends Applicative<Fn1<? super A, ? ex
      * {@inheritDoc}
      */
     @Override
-    public final <B> Maybe<B> discardL(Applicative<B, Maybe<?>> appB) {
+    default <B> Maybe<B> discardL(Applicative<B, Maybe<?>> appB) {
         return MonadError.super.discardL(appB).coerce();
     }
 
@@ -186,7 +183,7 @@ public final <B> Maybe<B> discardL(Applicative<B, Maybe<?>> appB) {
      * {@inheritDoc}
      */
     @Override
-    public final <B> Maybe<A> discardR(Applicative<B, Maybe<?>> appB) {
+    default <B> Maybe<A> discardR(Applicative<B, Maybe<?>> appB) {
         return MonadError.super.discardR(appB).coerce();
     }
 
@@ -195,7 +192,7 @@ public final <B> Maybe<A> discardR(Applicative<B, Maybe<?>> appB) {
      */
     @SuppressWarnings("RedundantTypeArguments")
     @Override
-    public final <B> Maybe<B> flatMap(Fn1<? super A, ? extends Monad<B, Maybe<?>>> f) {
+    default  <B> Maybe<B> flatMap(Fn1<? super A, ? extends Monad<B, Maybe<?>>> f) {
         return match(constantly(nothing()), f.fmap(Monad<B, Maybe<?>>::coerce));
     }
 
@@ -203,7 +200,7 @@ public final <B> Maybe<B> flatMap(Fn1<? super A, ? extends Monad<B, Maybe<?>>> f
      * {@inheritDoc}
      */
     @Override
-    public <B> Maybe<B> trampolineM(Fn1<? super A, ? extends MonadRec<RecursiveResult<A, B>, Maybe<?>>> fn) {
+    default <B> Maybe<B> trampolineM(Fn1<? super A, ? extends MonadRec<RecursiveResult<A, B>, Maybe<?>>> fn) {
         return match(constantly(nothing()), trampoline(a -> fn.apply(a).<Maybe<RecursiveResult<A, B>>>coerce()
                 .match(constantly(terminate(nothing())),
                        aOrB -> aOrB.fmap(Maybe::just))));
@@ -213,7 +210,7 @@ public <B> Maybe<B> trampolineM(Fn1<? super A, ? extends MonadRec<RecursiveResul
      * {@inheritDoc}
      */
     @Override
-    public <B> Choice3<Unit, A, B> diverge() {
+    default <B> Choice3<Unit, A, B> diverge() {
         return match(Choice3::a, Choice3::b);
     }
 
@@ -221,7 +218,7 @@ public <B> Choice3<Unit, A, B> diverge() {
      * {@inheritDoc}
      */
     @Override
-    public Tuple2<Maybe<Unit>, Maybe<A>> project() {
+    default Tuple2<Maybe<Unit>, Maybe<A>> project() {
         return CoProduct2.super.project().into(HList::tuple);
     }
 
@@ -229,7 +226,7 @@ public Tuple2<Maybe<Unit>, Maybe<A>> project() {
      * {@inheritDoc}
      */
     @Override
-    public Choice2<A, Unit> invert() {
+    default Choice2<A, Unit> invert() {
         return match(Choice2::b, Choice2::a);
     }
 
@@ -241,13 +238,13 @@ public Choice2<A, Unit> invert() {
      * @deprecated in favor of {@link Maybe#match(Fn1, Fn1) matching} into an {@link IO} and explicitly running it
      */
     @Deprecated
-    public final Maybe<A> peek(Fn1<? super A, ? extends IO<?>> effect) {
+    default Maybe<A> peek(Fn1<? super A, ? extends IO<?>> effect) {
         return match(constantly(io(this)), a -> effect.apply(a).fmap(constantly(this))).unsafePerformIO();
     }
 
     @Override
     @SuppressWarnings("unchecked")
-    public final <B, App extends Applicative<?, App>, TravB extends Traversable<B, Maybe<?>>,
+    default <B, App extends Applicative<?, App>, TravB extends Traversable<B, Maybe<?>>,
             AppTrav extends Applicative<TravB, App>> AppTrav traverse(Fn1<? super A, ? extends Applicative<B, App>> fn,
                                                                       Fn1<? super TravB, ? extends AppTrav> pure) {
         return match(__ -> pure.apply((TravB) Maybe.<B>nothing()), a -> (AppTrav) fn.apply(a).fmap(Maybe::just));
@@ -261,7 +258,7 @@ AppTrav extends Applicative<TravB, App>> AppTrav traverse(Fn1<? super A, ? exten
      * @param <A>    the potential right value
      * @return "Just" the right value, or nothing
      */
-    public static <A> Maybe<A> fromEither(Either<?, A> either) {
+    static <A> Maybe<A> fromEither(Either<?, A> either) {
         return either.toMaybe();
     }
 
@@ -272,7 +269,7 @@ public static <A> Maybe<A> fromEither(Either<?, A> either) {
      * @param <A>      the optional parameter type
      * @return the equivalent Maybe instance
      */
-    public static <A> Maybe<A> fromOptional(Optional<? extends A> optional) {
+    static <A> Maybe<A> fromOptional(Optional<? extends A> optional) {
         return optional.map(Maybe::<A>just).orElse(Maybe.nothing());
     }
 
@@ -284,7 +281,7 @@ public static <A> Maybe<A> fromOptional(Optional<? extends A> optional) {
      * @param <A> the value parameter type
      * @return "Just" the value, or nothing
      */
-    public static <A> Maybe<A> maybe(A a) {
+    static <A> Maybe<A> maybe(A a) {
         return a == null ? nothing() : just(a);
     }
 
@@ -297,7 +294,7 @@ public static <A> Maybe<A> maybe(A a) {
      * @return "Just" the value
      * @throws NullPointerException if a is null
      */
-    public static <A> Maybe<A> just(A a) {
+    static <A> Maybe<A> just(A a) {
         if (a == null)
             throw new NullPointerException();
         return new Just<>(a);
@@ -310,7 +307,7 @@ public static <A> Maybe<A> just(A a) {
      * @return nothing
      */
     @SuppressWarnings("unchecked")
-    public static <A> Maybe<A> nothing() {
+    static <A> Maybe<A> nothing() {
         return (Maybe<A>) Nothing.INSTANCE;
     }
 
@@ -319,53 +316,22 @@ public static <A> Maybe<A> nothing() {
      *
      * @return the {@link Pure} instance
      */
-    public static Pure<Maybe<?>> pureMaybe() {
+    static Pure<Maybe<?>> pureMaybe() {
         return Maybe::just;
     }
+}
+record Nothing<A>() implements Maybe<A> {
+    static final Nothing<?> INSTANCE = new Nothing<>();
 
-    private static final class Nothing<A> extends Maybe<A> {
-        private static final Nothing<?> INSTANCE = new Nothing<>();
-
-        private Nothing() {
-        }
-
-        @Override
-        public <R> R match(Fn1<? super Unit, ? extends R> aFn, Fn1<? super A, ? extends R> bFn) {
-            return aFn.apply(UNIT);
-        }
-
-        @Override
-        public String toString() {
-            return "Nothing";
-        }
+    @Override
+    public <R> R match(Fn1<? super Unit, ? extends R> aFn, Fn1<? super A, ? extends R> bFn) {
+        return aFn.apply(UNIT);
     }
+}
 
-    private static final class Just<A> extends Maybe<A> {
-
-        private final A a;
-
-        private Just(A a) {
-            this.a = a;
-        }
-
-        @Override
-        public <R> R match(Fn1<? super Unit, ? extends R> aFn, Fn1<? super A, ? extends R> bFn) {
-            return bFn.apply(a);
-        }
-
-        @Override
-        public boolean equals(Object other) {
-            return other instanceof Just && Objects.equals(this.a, ((Just) other).a);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(a);
-        }
-
-        @Override
-        public String toString() {
-            return "Just " + a;
-        }
+record Just<A>(A a) implements Maybe<A> {
+    @Override
+    public <R> R match(Fn1<? super Unit, ? extends R> aFn, Fn1<? super A, ? extends R> bFn) {
+        return bFn.apply(a);
     }
-}
+}
\ No newline at end of file