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 -> A</code>. * * @return an <code>{@link Fn1}<S, A></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}<B, T></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}<B, T></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}<S, {@link Either}<T, A>></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