From f30d63fb5c5367aba49b2e319fef50788504553a Mon Sep 17 00:00:00 2001 From: jnape Date: Sat, 8 Feb 2020 17:58:13 -0600 Subject: [PATCH 01/49] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f89d14be0..bda3d7caf 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ lambda - 5.2.0 + 5.2.1-SNAPSHOT jar Lambda From a04ea83f02819aa219c61d07d05352d9365fb27f Mon Sep 17 00:00:00 2001 From: jnape Date: Wed, 12 Feb 2020 18:17:05 -0600 Subject: [PATCH 02/49] Updating README and CHANGELOG --- CHANGELOG.md | 7 ++++++- README.md | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8561f1f8f..1ce4e47aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] +There are currently no unreleased changes + +## [5.2.0] - 2020-02-12 + ### Changed - `HList#cons` static factory method auto-promotes to specialized `HList` if there is one - `EitherT` gains a `MonadError` instance @@ -557,7 +561,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `Monadic/Dyadic/TriadicFunction`, `Predicate`, `Tuple2`, `Tuple3` - `Functor`, `BiFunctor`, `ProFunctor` -[Unreleased]: https://github.com/palatable/lambda/compare/lambda-5.1.0...HEAD +[Unreleased]: https://github.com/palatable/lambda/compare/lambda-5.2.0...HEAD +[5.2.0]: https://github.com/palatable/lambda/compare/lambda-5.1.0...lambda-5.2.0 [5.1.0]: https://github.com/palatable/lambda/compare/lambda-5.0.0...lambda-5.1.0 [5.0.0]: https://github.com/palatable/lambda/compare/lambda-4.0.0...lambda-5.0.0 [4.0.0]: https://github.com/palatable/lambda/compare/lambda-3.3.0...lambda-4.0.0 diff --git a/README.md b/README.md index edaf4dde9..312d220fb 100644 --- a/README.md +++ b/README.md @@ -61,14 +61,14 @@ Add the following dependency to your: com.jnape.palatable lambda - 5.1.0 + 5.2.0 ``` `build.gradle` ([Gradle](https://docs.gradle.org/current/userguide/dependency_management.html)): ```gradle -compile group: 'com.jnape.palatable', name: 'lambda', version: '5.1.0' +compile group: 'com.jnape.palatable', name: 'lambda', version: '5.2.0' ``` Examples From 24658def4d150ce13e0c3ff7073a8612cd21d42f Mon Sep 17 00:00:00 2001 From: jnape Date: Fri, 21 Feb 2020 15:44:30 -0600 Subject: [PATCH 03/49] Adding static Pure and Lift instances for IterateT --- .../monad/transformer/builtin/IterateT.java | 26 ++++++++++++++ .../transformer/builtin/IterateTTest.java | 35 +++++++++++-------- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java index 606e7d894..e4197b98d 100644 --- a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java +++ b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java @@ -8,6 +8,7 @@ import com.jnape.palatable.lambda.functions.Fn1; import com.jnape.palatable.lambda.functions.Fn2; import com.jnape.palatable.lambda.functions.recursion.RecursiveResult; +import com.jnape.palatable.lambda.functions.specialized.Lift; import com.jnape.palatable.lambda.functions.specialized.Pure; import com.jnape.palatable.lambda.functor.Applicative; import com.jnape.palatable.lambda.functor.builtin.Lazy; @@ -451,4 +452,29 @@ public static IterateT, A> fromIterator(Iterator as) { return nothing(); }), io(() -> as)); } + + /** + * The canonical {@link Pure} instance for {@link IterateT}. + * + * @param pureM the argument {@link Monad} {@link Pure} + * @param the argument {@link Monad} witness + * @return the {@link Pure} instance + */ + public static > Pure> pureIterateT(Pure pureM) { + return new Pure>() { + @Override + public IterateT checkedApply(A a) { + return liftIterateT().apply(pureM.>apply(a)); + } + }; + } + + /** + * {@link Lift} for {@link IterateT}. + * + * @return the {@link Monad} lifted into {@link IterateT} + */ + public static Lift> liftIterateT() { + return IterateT::singleton; + } } diff --git a/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java b/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java index 00768b0c3..b74e93835 100644 --- a/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java +++ b/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java @@ -13,11 +13,7 @@ import com.jnape.palatable.traitor.runners.Traits; import org.junit.Test; import org.junit.runner.RunWith; -import testsupport.traits.ApplicativeLaws; -import testsupport.traits.Equivalence; -import testsupport.traits.FunctorLaws; -import testsupport.traits.MonadLaws; -import testsupport.traits.MonadRecLaws; +import testsupport.traits.*; import java.util.ArrayList; import java.util.Collection; @@ -34,14 +30,9 @@ import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.terminate; import static com.jnape.palatable.lambda.functor.builtin.Identity.pureIdentity; import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy; -import static com.jnape.palatable.lambda.functor.builtin.Writer.listen; -import static com.jnape.palatable.lambda.functor.builtin.Writer.pureWriter; -import static com.jnape.palatable.lambda.functor.builtin.Writer.tell; -import static com.jnape.palatable.lambda.functor.builtin.Writer.writer; +import static com.jnape.palatable.lambda.functor.builtin.Writer.*; import static com.jnape.palatable.lambda.io.IO.io; -import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.empty; -import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.singleton; -import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.unfold; +import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.*; import static com.jnape.palatable.lambda.monoid.builtin.AddAll.addAll; import static com.jnape.palatable.lambda.monoid.builtin.Join.join; import static com.jnape.palatable.traitor.framework.Subjects.subjects; @@ -53,9 +44,7 @@ import static org.junit.Assert.assertThat; import static testsupport.Constants.STACK_EXPLODING_NUMBER; import static testsupport.matchers.IOMatcher.yieldsValue; -import static testsupport.matchers.IterateTMatcher.isEmpty; -import static testsupport.matchers.IterateTMatcher.iterates; -import static testsupport.matchers.IterateTMatcher.iteratesAll; +import static testsupport.matchers.IterateTMatcher.*; import static testsupport.traits.Equivalence.equivalence; @RunWith(Traits.class) @@ -242,4 +231,20 @@ public void concatIsStackSafe() { assertEquals(new Identity<>(10_000), bigIterateT.fold((x, y) -> new Identity<>(x + y), new Identity<>(0))); } + + @Test + public void staticPure() { + assertEquals(new Identity<>(singletonList(1)), + pureIterateT(pureIdentity()) + ., Integer>>apply(1) + ., Identity>>toCollection(ArrayList::new)); + } + + @Test + public void staticLift() { + assertEquals(new Identity<>(singletonList(1)), + liftIterateT() + ., IterateT, Integer>>apply(new Identity<>(1)) + ., Identity>>toCollection(ArrayList::new)); + } } \ No newline at end of file From 5cc409441d391d1fa1614000f82e7129350faaac Mon Sep 17 00:00:00 2001 From: John Napier Date: Mon, 23 Mar 2020 15:52:38 -0500 Subject: [PATCH 04/49] Attempting to add JDK14 build to github actions --- .github/workflows/maven.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 2f7615ccb..873d5ac83 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -28,3 +28,16 @@ jobs: java-version: 11 - name: Build with Maven run: mvn clean verify + + build-java-14: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: Set up JDK 14 + uses: actions/setup-java@v1 + with: + java-version: 14 + - name: Build with Maven + run: mvn clean verify From 28f92b412d357e6516d3002ef816fb00c90ad685 Mon Sep 17 00:00:00 2001 From: jnape Date: Wed, 1 Apr 2020 14:34:23 -0500 Subject: [PATCH 05/49] Adding $, function application represented as a higher-order Fn2 --- CHANGELOG.md | 3 +- .../lambda/functions/builtin/fn2/$.java | 43 +++++++++++++++++++ .../lambda/functions/builtin/fn2/$Test.java | 21 +++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/$.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/$Test.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ce4e47aa..885221573 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] -There are currently no unreleased changes +### Added +- `$`, function application represented as a higher-order `Fn2` ## [5.2.0] - 2020-02-12 diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/$.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/$.java new file mode 100644 index 000000000..63c60c051 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/$.java @@ -0,0 +1,43 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.Fn2; + +/** + * Function application, represented as a higher-order {@link Fn2} that receives an {@link Fn1} and its argument, and + * applies it. Useful for treating application as a combinator, e.g.: + *
+ * {@code
+ * List> fns     = asList(x -> x + 1, x -> x, x -> x - 1);
+ * List               args    = asList(0, 1, 2);
+ * Iterable           results = zipWith($(), fns, args); // [1, 1, 1]
+ * }
+ * 
+ * + * @param
the applied {@link Fn1 Fn1's} input type + * @param the applied {@link Fn1 Fn1's} output type + */ +public final class $ implements Fn2, A, B> { + private static final $ INSTANCE = new $<>(); + + private $() { + } + + @Override + public B checkedApply(Fn1 fn, A a) { + return fn.apply(a); + } + + @SuppressWarnings("unchecked") + public static $ $() { + return ($) INSTANCE; + } + + public static Fn1 $(Fn1 fn) { + return $.$().apply(fn); + } + + public static B $(Fn1 fn, A a) { + return $.$(fn).apply(a); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/$Test.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/$Test.java new file mode 100644 index 000000000..166e99f29 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/$Test.java @@ -0,0 +1,21 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.functions.Fn2.fn2; +import static com.jnape.palatable.lambda.functions.builtin.fn2.$.$; +import static org.junit.Assert.assertEquals; + +public class $Test { + + @Test + public void application() { + assertEquals((Integer) 1, $(x -> x + 1, 0)); + assertEquals((Integer) 1, $.$(x -> x + 1).apply(0)); + } + + @Test + public void curryingInference() { + assertEquals((Integer) 1, $($(fn2(Integer::sum), 0), 1)); + } +} \ No newline at end of file From e9a98fc6ef6db5b1c2ac4c892adb9deebabb073f Mon Sep 17 00:00:00 2001 From: Joshua Potts <8704475+iamjpotts@users.noreply.github.com> Date: Sun, 12 Apr 2020 23:36:51 -0500 Subject: [PATCH 06/49] Adjust documentation for Fn7 and Fn8 --- src/main/java/com/jnape/palatable/lambda/functions/Fn7.java | 2 +- src/main/java/com/jnape/palatable/lambda/functions/Fn8.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn7.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn7.java index 2d27e6be2..88044dff1 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/Fn7.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn7.java @@ -8,7 +8,7 @@ import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly; /** - * A function taking six arguments. Defined in terms of {@link Fn6}, so similarly auto-curried. + * A function taking seven arguments. Defined in terms of {@link Fn6}, so similarly auto-curried. * * @param The first argument type * @param The second argument type diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn8.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn8.java index a38e74eb1..c2b8fab52 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/Fn8.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn8.java @@ -5,7 +5,7 @@ import com.jnape.palatable.lambda.internal.Runtime; /** - * A function taking six arguments. Defined in terms of {@link Fn7}, so similarly auto-curried. + * A function taking eight arguments. Defined in terms of {@link Fn7}, so similarly auto-curried. * * @param The first argument type * @param The second argument type From 10a229b7fc3b88c2d6bd37ec45f877aefaf37864 Mon Sep 17 00:00:00 2001 From: Joshua Potts <8704475+iamjpotts@users.noreply.github.com> Date: Tue, 14 Apr 2020 22:17:54 -0500 Subject: [PATCH 07/49] Adjust documentation for Fn7 and Fn8 --- src/main/java/com/jnape/palatable/lambda/functions/Fn7.java | 4 ++-- src/main/java/com/jnape/palatable/lambda/functions/Fn8.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn7.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn7.java index 88044dff1..15f7a3be9 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/Fn7.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn7.java @@ -258,7 +258,7 @@ static Fn7 fn7(Fn4 the first input argument type * @param the second input argument type * @param the third input argument type @@ -276,7 +276,7 @@ static Fn7 fn7(Fn5 the first input argument type * @param the second input argument type * @param the third input argument type diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn8.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn8.java index c2b8fab52..60f3a4a3c 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/Fn8.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn8.java @@ -274,7 +274,7 @@ static Fn8 fn8( /** * Static factory method for wrapping a curried {@link Fn5} in an {@link Fn8}. * - * @param curriedFn5 the curried fn4 to adapt + * @param curriedFn5 the curried fn5 to adapt * @param the first input argument type * @param the second input argument type * @param the third input argument type @@ -294,7 +294,7 @@ static Fn8 fn8( /** * Static factory method for wrapping a curried {@link Fn6} in an {@link Fn8}. * - * @param curriedFn6 the curried fn4 to adapt + * @param curriedFn6 the curried fn6 to adapt * @param the first input argument type * @param the second input argument type * @param the third input argument type @@ -314,7 +314,7 @@ static Fn8 fn8( /** * Static factory method for wrapping a curried {@link Fn7} in an {@link Fn8}. * - * @param curriedFn7 the curried fn4 to adapt + * @param curriedFn7 the curried fn7 to adapt * @param the first input argument type * @param the second input argument type * @param the third input argument type From 3e6f644320bfe0a06f8207f3e2ff5b9d38c0688f Mon Sep 17 00:00:00 2001 From: jnape Date: Wed, 22 Apr 2020 17:26:51 -0500 Subject: [PATCH 08/49] Fn1#withSelf for writing self-referencing Fn1s --- CHANGELOG.md | 1 + .../jnape/palatable/lambda/functions/Fn1.java | 17 +++++++++++++++++ .../palatable/lambda/functions/Fn1Test.java | 7 ++++++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 885221573..c0b802288 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ### Added - `$`, function application represented as a higher-order `Fn2` +- `Fn1#withSelf`, a static method for constructing a self-referencing `Fn1` ## [5.2.0] - 2020-02-12 diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn1.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn1.java index f21e70e23..cb4904d35 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/Fn1.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn1.java @@ -302,6 +302,10 @@ default Fn2 andThen(Fn2 after return (a, c) -> after.apply(apply(a), c); } + default Fn1 self() { + return this; + } + /** * Static factory method for avoid explicit casting when using method references as {@link Fn1}s. * @@ -335,4 +339,17 @@ static Fn1 fromFunction(Function function) static Pure> pureFn1() { return Constantly::constantly; } + + /** + * Construct an {@link Fn1} that has a reference to itself in scope at the time it is executed (presumably for + * recursive invocations). + * + * @param fn the body of the function, with access to itself + * @param the input type + * @param the output type + * @return the {@link Fn1} + */ + static Fn1 withSelf(Fn2, ? super A, ? extends B> fn) { + return a -> fn.apply(withSelf(fn), a); + } } diff --git a/src/test/java/com/jnape/palatable/lambda/functions/Fn1Test.java b/src/test/java/com/jnape/palatable/lambda/functions/Fn1Test.java index 39400c6b0..047aea1db 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/Fn1Test.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/Fn1Test.java @@ -8,8 +8,8 @@ import testsupport.traits.Equivalence; import testsupport.traits.FunctorLaws; import testsupport.traits.MonadLaws; -import testsupport.traits.MonadRecLaws; import testsupport.traits.MonadReaderLaws; +import testsupport.traits.MonadRecLaws; import testsupport.traits.MonadWriterLaws; import java.util.function.Function; @@ -105,4 +105,9 @@ public void staticPure() { Fn1 fn1 = Fn1.pureFn1().apply(1); assertEquals((Integer) 1, fn1.apply("anything")); } + + @Test + public void withSelf() { + assertEquals((Integer) 15, Fn1.withSelf((f, x) -> x > 1 ? x + f.apply(x - 1) : x).apply(5)); + } } From 27068677d6b0e9ac0665cecb72a209beee43c5e0 Mon Sep 17 00:00:00 2001 From: jnape Date: Wed, 22 Apr 2020 17:27:38 -0500 Subject: [PATCH 09/49] IterateT#unfold now only constructs one Pure instance at invocation time --- .../monad/transformer/builtin/IterateT.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java index e4197b98d..07ea19982 100644 --- a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java +++ b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java @@ -29,6 +29,8 @@ import static com.jnape.palatable.lambda.adt.choice.Choice2.a; import static com.jnape.palatable.lambda.adt.choice.Choice2.b; import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.Fn1.withSelf; +import static com.jnape.palatable.lambda.functions.builtin.fn2.$.$; import static com.jnape.palatable.lambda.functions.builtin.fn2.Into.into; import static com.jnape.palatable.lambda.functions.builtin.fn2.Tupler2.tupler; import static com.jnape.palatable.lambda.functions.builtin.fn3.FoldLeft.foldLeft; @@ -74,10 +76,10 @@ private IterateT(Pure pureM, ImmutableQueue> conses, ImmutableQueue>>, M>>, IterateT>> middles, ImmutableQueue> snocs) { - this.pureM = pureM; - this.conses = conses; + this.pureM = pureM; + this.conses = conses; this.middles = middles; - this.snocs = snocs; + this.snocs = snocs; } /** @@ -220,7 +222,7 @@ public IterateT fmap(Fn1 fn) { */ @Override public IterateT pure(B b) { - return singleton(runIterateT().pure(b)); + return singleton(pureM.>apply(b)); } /** @@ -413,9 +415,10 @@ public static , A> IterateT of( */ public static , A, B> IterateT unfold( Fn1>, M>> fn, MonadRec mb) { - return suspended(() -> maybeT(mb.flatMap(fn)) - .fmap(ab -> ab.fmap(b -> unfold(fn, mb.pure(b)))) - .runMaybeT(), Pure.of(mb)); + Pure pureM = Pure.of(mb); + return $(withSelf((self, mmb) -> suspended(() -> maybeT(mmb.flatMap(fn)) + .fmap(ab -> ab.>fmap(b -> self.apply(pureM.apply(b)))) + .runMaybeT(), pureM)), mb); } /** From 66b045930be1fc1c18ddced61bca23e42f4a3ce7 Mon Sep 17 00:00:00 2001 From: jnape Date: Wed, 22 Apr 2020 18:12:14 -0500 Subject: [PATCH 10/49] Updating CHANGELOG --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0b802288..281562d22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] +### Changed +- `IterateT#unfold` now only computes a single `Pure` for the given input + ### Added - `$`, function application represented as a higher-order `Fn2` - `Fn1#withSelf`, a static method for constructing a self-referencing `Fn1` From 7ec5690d53e20ef6cde3e70b128c50f27650e46c Mon Sep 17 00:00:00 2001 From: Joshua Potts <8704475+iamjpotts@users.noreply.github.com> Date: Tue, 14 Apr 2020 20:32:53 -0500 Subject: [PATCH 11/49] Add snoc to HNil, SingletonHList, and Tuple classes --- CHANGELOG.md | 1 + .../com/jnape/palatable/lambda/adt/hlist/HList.java | 11 +++++++++++ .../palatable/lambda/adt/hlist/SingletonHList.java | 11 +++++++++++ .../com/jnape/palatable/lambda/adt/hlist/Tuple2.java | 10 ++++++++++ .../com/jnape/palatable/lambda/adt/hlist/Tuple3.java | 10 ++++++++++ .../com/jnape/palatable/lambda/adt/hlist/Tuple4.java | 10 ++++++++++ .../com/jnape/palatable/lambda/adt/hlist/Tuple5.java | 10 ++++++++++ .../com/jnape/palatable/lambda/adt/hlist/Tuple6.java | 10 ++++++++++ .../com/jnape/palatable/lambda/adt/hlist/Tuple7.java | 10 ++++++++++ .../com/jnape/palatable/lambda/adt/hlist/Tuple8.java | 10 ++++++++++ .../jnape/palatable/lambda/adt/hlist/HListTest.java | 6 ++++++ .../lambda/adt/hlist/SingletonHListTest.java | 7 +++++++ .../jnape/palatable/lambda/adt/hlist/Tuple2Test.java | 6 ++++++ .../jnape/palatable/lambda/adt/hlist/Tuple3Test.java | 9 +++++++++ .../jnape/palatable/lambda/adt/hlist/Tuple4Test.java | 6 ++++++ .../jnape/palatable/lambda/adt/hlist/Tuple5Test.java | 6 ++++++ .../jnape/palatable/lambda/adt/hlist/Tuple6Test.java | 6 ++++++ .../jnape/palatable/lambda/adt/hlist/Tuple7Test.java | 6 ++++++ .../jnape/palatable/lambda/adt/hlist/Tuple8Test.java | 9 +++++++++ 19 files changed, 154 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 281562d22..e96c8697d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ### Added - `$`, function application represented as a higher-order `Fn2` - `Fn1#withSelf`, a static method for constructing a self-referencing `Fn1` +- `HNil/SingletonHList/TupleX#snoc`, a method to add a new last element (append to a tuple) ## [5.2.0] - 2020-02-12 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 421855a10..897f19c05 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 @@ -297,5 +297,16 @@ private HNil() { public SingletonHList cons(Head head) { return new SingletonHList<>(head); } + + /** + * Snoc an element onto the back of this HList. + * + * @param last the new last element + * @param the new last element type + * @return the updated HList + */ + public SingletonHList snoc(Last last) { + return new SingletonHList<>(last); + } } } diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/SingletonHList.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/SingletonHList.java index 927f27ae3..79b3fbc87 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/SingletonHList.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/SingletonHList.java @@ -39,6 +39,17 @@ public <_0> Tuple2<_0, _1> cons(_0 _0) { return new Tuple2<>(_0, this); } + + /** + * Snoc an element onto the back of this HList. + * + * @param _2 the new last element + * @return the updated HList + */ + public <_2> Tuple2<_1, _2> snoc(_2 _2) { + return tuple(head(), _2); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java index 726335f6d..b08d096f2 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java @@ -75,6 +75,16 @@ public <_0> Tuple3<_0, _1, _2> cons(_0 _0) { return new Tuple3<>(_0, this); } + /** + * Snoc an element onto the back of this HList. + * + * @param _3 the new last element + * @return the updated HList + */ + public <_3> Tuple3<_1, _2, _3> snoc(_3 _3) { + return tuple(_1, _2, _3); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.java index e32eac589..48fd309a4 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.java @@ -56,6 +56,16 @@ public <_0> Tuple4<_0, _1, _2, _3> cons(_0 _0) { return new Tuple4<>(_0, this); } + /** + * Snoc an element onto the back of this HList. + * + * @param _4 the new last element + * @return the updated HList + */ + public <_4> Tuple4<_1, _2, _3, _4> snoc(_4 _4) { + return tuple(_1, _2, _3, _4); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.java index 3e74da859..f8b630aa5 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.java @@ -59,6 +59,16 @@ public <_0> Tuple5<_0, _1, _2, _3, _4> cons(_0 _0) { return new Tuple5<>(_0, this); } + /** + * Snoc an element onto the back of this HList. + * + * @param _5 the new last element + * @return the updated HList + */ + public <_5> Tuple5<_1, _2, _3, _4, _5> snoc(_5 _5) { + return tuple(_1, _2, _3, _4, _5); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java index c87ee564b..e7a2c7877 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java @@ -62,6 +62,16 @@ public <_0> Tuple6<_0, _1, _2, _3, _4, _5> cons(_0 _0) { return new Tuple6<>(_0, this); } + /** + * Snoc an element onto the back of this HList. + * + * @param _6 the new last element + * @return the updated HList + */ + public <_6> Tuple6<_1, _2, _3, _4, _5, _6> snoc(_6 _6) { + return tuple(_1, _2, _3, _4, _5, _6); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java index 6d07b503b..ca65c7793 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java @@ -66,6 +66,16 @@ public <_0> Tuple7<_0, _1, _2, _3, _4, _5, _6> cons(_0 _0) { return new Tuple7<>(_0, this); } + /** + * Snoc an element onto the back of this HList. + * + * @param _7 the new last element + * @return the updated HList + */ + public <_7> Tuple7<_1, _2, _3, _4, _5, _6, _7> snoc(_7 _7) { + return tuple(_1, _2, _3, _4, _5, _6, _7); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java index a3e162f73..61f4edf63 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java @@ -70,6 +70,16 @@ public <_0> Tuple8<_0, _1, _2, _3, _4, _5, _6, _7> cons(_0 _0) { return new Tuple8<>(_0, this); } + /** + * Snoc an element onto the back of this HList. + * + * @param _8 the new last element + * @return the updated HList + */ + public <_8> Tuple8<_1, _2, _3, _4, _5, _6, _7, _8> snoc(_8 _8) { + return tuple(_1, _2, _3, _4, _5, _6, _7, _8); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java index 8a8001e24..fd6f3d8d0 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java @@ -74,6 +74,16 @@ public <_0> HCons<_0, Tuple8<_1, _2, _3, _4, _5, _6, _7, _8>> cons(_0 _0) { return new HCons<>(_0, this); } + /** + * Snoc an element onto the back of this HList. + * + * @param _9 the new last element + * @return the updated HList + */ + public <_9> HCons<_1, Tuple8<_2, _3, _4, _5, _6, _7, _8, _9>> snoc(_9 _9) { + return singletonHList(_9).cons(_8).cons(_7).cons(_6).cons(_5).cons(_4).cons(_3).cons(_2).cons(_1); + } + /** * {@inheritDoc} */ diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/HListTest.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/HListTest.java index 1f75ce450..09e9e44d0 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/HListTest.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/HListTest.java @@ -68,4 +68,10 @@ public void hashCodeUsesDecentDistribution() { assertNotEquals(nil().cons(1).hashCode(), nil().cons(2).hashCode()); assertNotEquals(nil().cons(1).cons(2).hashCode(), nil().cons(1).cons(3).hashCode()); } + + @Test + public void snoc() { + SingletonHList tuple = nil().snoc((float) 4.0); + assertEquals(4.0, tuple.head(), 0.01); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/SingletonHListTest.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/SingletonHListTest.java index 57a9f85b7..6d7d8c0b2 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/SingletonHListTest.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/SingletonHListTest.java @@ -13,6 +13,7 @@ import static com.jnape.palatable.lambda.adt.hlist.HList.nil; import static com.jnape.palatable.lambda.adt.hlist.HList.singletonHList; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.adt.hlist.SingletonHList.pureSingletonHList; import static org.junit.Assert.assertEquals; @@ -56,4 +57,10 @@ public void staticPure() { SingletonHList singletonHList = pureSingletonHList().apply(1); assertEquals(singletonHList(1), singletonHList); } + + @Test + public void snoc() { + Tuple2 tuple = singletonHList((byte) 127).snoc('x'); + assertEquals(tuple((byte) 127, 'x'), tuple); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java index 32483a168..ce28b964b 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java @@ -143,4 +143,10 @@ public void staticPure() { Tuple2 tuple = pureTuple(1).apply("two"); assertEquals(tuple(1, "two"), tuple); } + + @Test + public void snoc() { + Tuple3 tuple = tuple(Long.MAX_VALUE, 123).snoc("hi"); + assertEquals(tuple(Long.MAX_VALUE, 123, "hi"), tuple); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java index 9ac57fcfb..aa25c3e59 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java @@ -13,11 +13,14 @@ import testsupport.traits.MonadRecLaws; import testsupport.traits.TraversableLaws; +import java.time.Duration; + import static com.jnape.palatable.lambda.adt.Maybe.just; import static com.jnape.palatable.lambda.adt.Maybe.nothing; import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.adt.hlist.Tuple3.pureTuple; import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat; +import static java.time.Duration.ofSeconds; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.junit.Assert.assertEquals; @@ -119,4 +122,10 @@ public void staticPure() { Tuple3 tuple = pureTuple(1, "2").apply('3'); assertEquals(tuple(1, "2", '3'), tuple); } + + @Test + public void snoc() { + Tuple4 tuple = tuple("qux", Long.MIN_VALUE, 7).snoc(ofSeconds(13)); + assertEquals(tuple("qux", Long.MIN_VALUE, 7, ofSeconds(13)), tuple); + } } diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java index b2a1d6319..a0b27f05e 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java @@ -122,4 +122,10 @@ public void staticPure() { Tuple4 tuple = pureTuple(1, "2", '3').apply(true); assertEquals(tuple(1, "2", '3', true), tuple); } + + @Test + public void snoc() { + Tuple5 tuple = tuple("qux", 7, "foo", 13L).snoc(17); + assertEquals(tuple("qux", 7, "foo", 13L, 17), tuple); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java index 83b300b97..ee0d6b7be 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java @@ -128,4 +128,10 @@ public void staticPure() { Tuple5 tuple = pureTuple(1, "2", '3', true).apply(5f); assertEquals(tuple(1, "2", '3', true, 5f), tuple); } + + @Test + public void snoc() { + Tuple6 tuple = tuple("a", 5, "b", 7, "c").snoc(11); + assertEquals(tuple("a", 5, "b", 7, "c", 11), tuple); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java index 902b4b254..dd57fde9c 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java @@ -132,4 +132,10 @@ public void staticPure() { Tuple6 tuple = pureTuple(1, "2", '3', true, 5f).apply((byte) 6); assertEquals(tuple(1, "2", '3', true, 5f, (byte) 6), tuple); } + + @Test + public void snoc() { + Tuple7 tuple = tuple(5L, "a", 7, "b", 11, "c").snoc(13); + assertEquals(tuple(5L, "a", 7, "b", 11, "c", 13), tuple); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java index 0e8a68aec..14ccb30a6 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java @@ -136,4 +136,10 @@ public void staticPure() { pureTuple((byte) 1, (short) 2, 3, 4L, 5F, 6D).apply(true); assertEquals(tuple((byte) 1, (short) 2, 3, 4L, 5F, 6D, true), tuple); } + + @Test + public void snoc() { + Tuple8 tuple = tuple("b", 7L, "c", 11, "d", 13, "e").snoc('f'); + assertEquals(tuple("b", 7L, "c", 11, "d", 13, "e", 'f'), tuple); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java index 4d2a8cb76..33921079e 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java @@ -14,6 +14,8 @@ import testsupport.traits.MonadRecLaws; import testsupport.traits.TraversableLaws; +import java.time.LocalDate; + import static com.jnape.palatable.lambda.adt.Maybe.just; import static com.jnape.palatable.lambda.adt.Maybe.nothing; import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; @@ -147,4 +149,11 @@ public void staticPure() { pureTuple((byte) 1, (short) 2, 3, 4L, 5F, 6D, true).apply('8'); assertEquals(tuple((byte) 1, (short) 2, 3, 4L, 5F, 6D, true, '8'), tuple); } + + @Test + public void snoc() { + HCons> actual = tuple("b", 7L, "c", 11, "d", 13, "e", 15L).snoc(LocalDate.of(2020, 4, 14)); + assertEquals("b", actual.head()); + assertEquals(actual.tail(), tuple(7L, "c", 11, "d", 13, "e", 15L, LocalDate.of(2020, 4, 14))); + } } \ No newline at end of file From 8e95a1d09e4df6e07024a26b38107bd936712f3f Mon Sep 17 00:00:00 2001 From: jnape Date: Thu, 18 Jun 2020 11:23:09 -0500 Subject: [PATCH 12/49] Updating javadoc, reorganizing code, dropping HNil#snoc --- .../palatable/lambda/adt/hlist/HList.java | 20 +------------ .../lambda/adt/hlist/SingletonHList.java | 5 ++-- .../palatable/lambda/adt/hlist/Tuple2.java | 7 +++-- .../palatable/lambda/adt/hlist/Tuple3.java | 9 +++--- .../palatable/lambda/adt/hlist/Tuple4.java | 11 +++---- .../palatable/lambda/adt/hlist/Tuple5.java | 13 +++++---- .../palatable/lambda/adt/hlist/Tuple6.java | 15 +++++----- .../palatable/lambda/adt/hlist/Tuple7.java | 17 ++++++----- .../palatable/lambda/adt/hlist/Tuple8.java | 19 ++++++------ .../palatable/lambda/adt/hlist/HListTest.java | 29 +++++++++---------- .../lambda/adt/hlist/SingletonHListTest.java | 11 ++++--- .../lambda/adt/hlist/Tuple2Test.java | 11 ++++--- .../lambda/adt/hlist/Tuple3Test.java | 14 ++++----- .../lambda/adt/hlist/Tuple4Test.java | 11 ++++--- .../lambda/adt/hlist/Tuple5Test.java | 11 ++++--- .../lambda/adt/hlist/Tuple6Test.java | 11 ++++--- .../lambda/adt/hlist/Tuple7Test.java | 11 ++++--- .../lambda/adt/hlist/Tuple8Test.java | 16 +++++----- 18 files changed, 112 insertions(+), 129 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 897f19c05..705e43bad 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 @@ -89,7 +89,6 @@ public static SingletonHList singletonHList(Head head) { * @return the 2-element HList * @see Tuple2 */ - @SuppressWarnings("JavaDoc") public static <_1, _2> Tuple2<_1, _2> tuple(_1 _1, _2 _2) { return singletonHList(_2).cons(_1); } @@ -106,7 +105,6 @@ public static <_1, _2> Tuple2<_1, _2> tuple(_1 _1, _2 _2) { * @return the 3-element HList * @see Tuple3 */ - @SuppressWarnings("JavaDoc") public static <_1, _2, _3> Tuple3<_1, _2, _3> tuple(_1 _1, _2 _2, _3 _3) { return tuple(_2, _3).cons(_1); } @@ -125,7 +123,6 @@ public static <_1, _2, _3> Tuple3<_1, _2, _3> tuple(_1 _1, _2 _2, _3 _3) { * @return the 4-element HList * @see Tuple4 */ - @SuppressWarnings("JavaDoc") public static <_1, _2, _3, _4> Tuple4<_1, _2, _3, _4> tuple(_1 _1, _2 _2, _3 _3, _4 _4) { return tuple(_2, _3, _4).cons(_1); } @@ -146,7 +143,6 @@ public static <_1, _2, _3, _4> Tuple4<_1, _2, _3, _4> tuple(_1 _1, _2 _2, _3 _3, * @return the 5-element HList * @see Tuple5 */ - @SuppressWarnings("JavaDoc") public static <_1, _2, _3, _4, _5> Tuple5<_1, _2, _3, _4, _5> tuple(_1 _1, _2 _2, _3 _3, _4 _4, _5 _5) { return tuple(_2, _3, _4, _5).cons(_1); } @@ -169,7 +165,6 @@ public static <_1, _2, _3, _4, _5> Tuple5<_1, _2, _3, _4, _5> tuple(_1 _1, _2 _2 * @return the 6-element HList * @see Tuple6 */ - @SuppressWarnings("JavaDoc") public static <_1, _2, _3, _4, _5, _6> Tuple6<_1, _2, _3, _4, _5, _6> tuple(_1 _1, _2 _2, _3 _3, _4 _4, _5 _5, _6 _6) { return tuple(_2, _3, _4, _5, _6).cons(_1); @@ -195,7 +190,6 @@ public static <_1, _2, _3, _4, _5, _6> Tuple6<_1, _2, _3, _4, _5, _6> tuple(_1 _ * @return the 7-element HList * @see Tuple7 */ - @SuppressWarnings("JavaDoc") public static <_1, _2, _3, _4, _5, _6, _7> Tuple7<_1, _2, _3, _4, _5, _6, _7> tuple(_1 _1, _2 _2, _3 _3, _4 _4, _5 _5, _6 _6, _7 _7) { return tuple(_2, _3, _4, _5, _6, _7).cons(_1); @@ -223,7 +217,6 @@ public static <_1, _2, _3, _4, _5, _6, _7> Tuple7<_1, _2, _3, _4, _5, _6, _7> tu * @return the 8-element HList * @see Tuple8 */ - @SuppressWarnings("JavaDoc") public static <_1, _2, _3, _4, _5, _6, _7, _8> Tuple8<_1, _2, _3, _4, _5, _6, _7, _8> tuple(_1 _1, _2 _2, _3 _3, _4 _4, _5 _5, _6 _6, _7 _7, _8 _8) { @@ -273,7 +266,7 @@ public final boolean equals(Object other) { if (other instanceof HCons) { HCons that = (HCons) other; return this.head.equals(that.head) - && this.tail.equals(that.tail); + && this.tail.equals(that.tail); } return false; } @@ -297,16 +290,5 @@ private HNil() { public SingletonHList cons(Head head) { return new SingletonHList<>(head); } - - /** - * Snoc an element onto the back of this HList. - * - * @param last the new last element - * @param the new last element type - * @return the updated HList - */ - public SingletonHList snoc(Last last) { - return new SingletonHList<>(last); - } } } diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/SingletonHList.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/SingletonHList.java index 79b3fbc87..8e8516a47 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/SingletonHList.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/SingletonHList.java @@ -41,10 +41,11 @@ public <_0> Tuple2<_0, _1> cons(_0 _0) { /** - * Snoc an element onto the back of this HList. + * Snoc an element onto the back of this {@link SingletonHList}. * * @param _2 the new last element - * @return the updated HList + * @param <_2> the new last element type + * @return the new {@link Tuple2} */ public <_2> Tuple2<_1, _2> snoc(_2 _2) { return tuple(head(), _2); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java index b08d096f2..cdf79be12 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java @@ -48,7 +48,7 @@ public class Tuple2<_1, _2> extends HCons<_1, SingletonHList<_2>> implements Tuple2(_1 _1, SingletonHList<_2> tail) { super(_1, tail); this._1 = _1; - _2 = tail.head(); + _2 = tail.head(); } /** @@ -76,10 +76,11 @@ public <_0> Tuple3<_0, _1, _2> cons(_0 _0) { } /** - * Snoc an element onto the back of this HList. + * Snoc an element onto the back of this {@link Tuple2}. * * @param _3 the new last element - * @return the updated HList + * @param <_3> the new last element type + * @return the new {@link Tuple3} */ public <_3> Tuple3<_1, _2, _3> snoc(_3 _3) { return tuple(_1, _2, _3); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.java index 48fd309a4..6fe7710b3 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.java @@ -44,8 +44,8 @@ public class Tuple3<_1, _2, _3> extends HCons<_1, Tuple2<_2, _3>> implements Tuple3(_1 _1, Tuple2<_2, _3> tail) { super(_1, tail); this._1 = _1; - _2 = tail._1(); - _3 = tail._2(); + _2 = tail._1(); + _3 = tail._2(); } /** @@ -57,10 +57,11 @@ public <_0> Tuple4<_0, _1, _2, _3> cons(_0 _0) { } /** - * Snoc an element onto the back of this HList. + * Snoc an element onto the back of this {@link Tuple3}. * * @param _4 the new last element - * @return the updated HList + * @param <_4> the new last element type + * @return the new {@link Tuple4} */ public <_4> Tuple4<_1, _2, _3, _4> snoc(_4 _4) { return tuple(_1, _2, _3, _4); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.java index f8b630aa5..ebd946548 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.java @@ -46,9 +46,9 @@ public class Tuple4<_1, _2, _3, _4> extends HCons<_1, Tuple3<_2, _3, _4>> implem Tuple4(_1 _1, Tuple3<_2, _3, _4> tail) { super(_1, tail); this._1 = _1; - _2 = tail._1(); - _3 = tail._2(); - _4 = tail._3(); + _2 = tail._1(); + _3 = tail._2(); + _4 = tail._3(); } /** @@ -60,10 +60,11 @@ public <_0> Tuple5<_0, _1, _2, _3, _4> cons(_0 _0) { } /** - * Snoc an element onto the back of this HList. + * Snoc an element onto the back of this {@link Tuple4}. * * @param _5 the new last element - * @return the updated HList + * @param <_5> the new last element type + * @return the new {@link Tuple5} */ public <_5> Tuple5<_1, _2, _3, _4, _5> snoc(_5 _5) { return tuple(_1, _2, _3, _4, _5); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java index e7a2c7877..869a41f97 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java @@ -48,10 +48,10 @@ public class Tuple5<_1, _2, _3, _4, _5> extends HCons<_1, Tuple4<_2, _3, _4, _5> Tuple5(_1 _1, Tuple4<_2, _3, _4, _5> tail) { super(_1, tail); this._1 = _1; - _2 = tail._1(); - _3 = tail._2(); - _4 = tail._3(); - _5 = tail._4(); + _2 = tail._1(); + _3 = tail._2(); + _4 = tail._3(); + _5 = tail._4(); } /** @@ -63,10 +63,11 @@ public <_0> Tuple6<_0, _1, _2, _3, _4, _5> cons(_0 _0) { } /** - * Snoc an element onto the back of this HList. + * Snoc an element onto the back of this {@link Tuple5}. * * @param _6 the new last element - * @return the updated HList + * @param <_6> the new last element type + * @return the new {@link Tuple6} */ public <_6> Tuple6<_1, _2, _3, _4, _5, _6> snoc(_6 _6) { return tuple(_1, _2, _3, _4, _5, _6); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java index ca65c7793..72bbac9ba 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java @@ -51,11 +51,11 @@ public class Tuple6<_1, _2, _3, _4, _5, _6> extends HCons<_1, Tuple5<_2, _3, _4, Tuple6(_1 _1, Tuple5<_2, _3, _4, _5, _6> tail) { super(_1, tail); this._1 = _1; - _2 = tail._1(); - _3 = tail._2(); - _4 = tail._3(); - _5 = tail._4(); - _6 = tail._5(); + _2 = tail._1(); + _3 = tail._2(); + _4 = tail._3(); + _5 = tail._4(); + _6 = tail._5(); } /** @@ -67,10 +67,11 @@ public <_0> Tuple7<_0, _1, _2, _3, _4, _5, _6> cons(_0 _0) { } /** - * Snoc an element onto the back of this HList. + * Snoc an element onto the back of this {@link Tuple6}. * * @param _7 the new last element - * @return the updated HList + * @param <_7> the new last element type + * @return the new {@link Tuple7} */ public <_7> Tuple7<_1, _2, _3, _4, _5, _6, _7> snoc(_7 _7) { return tuple(_1, _2, _3, _4, _5, _6, _7); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java index 61f4edf63..3a2f027b7 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java @@ -54,12 +54,12 @@ public class Tuple7<_1, _2, _3, _4, _5, _6, _7> extends HCons<_1, Tuple6<_2, _3, Tuple7(_1 _1, Tuple6<_2, _3, _4, _5, _6, _7> tail) { super(_1, tail); this._1 = _1; - _2 = tail._1(); - _3 = tail._2(); - _4 = tail._3(); - _5 = tail._4(); - _6 = tail._5(); - _7 = tail._6(); + _2 = tail._1(); + _3 = tail._2(); + _4 = tail._3(); + _5 = tail._4(); + _6 = tail._5(); + _7 = tail._6(); } /** @@ -71,10 +71,11 @@ public <_0> Tuple8<_0, _1, _2, _3, _4, _5, _6, _7> cons(_0 _0) { } /** - * Snoc an element onto the back of this HList. + * Snoc an element onto the back of this {@link Tuple7}. * * @param _8 the new last element - * @return the updated HList + * @param <_8> the new last element type + * @return the new {@link Tuple8} */ public <_8> Tuple8<_1, _2, _3, _4, _5, _6, _7, _8> snoc(_8 _8) { return tuple(_1, _2, _3, _4, _5, _6, _7, _8); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java index fd6f3d8d0..c97cea46d 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java @@ -57,13 +57,13 @@ public class Tuple8<_1, _2, _3, _4, _5, _6, _7, _8> extends HCons<_1, Tuple7<_2, Tuple8(_1 _1, Tuple7<_2, _3, _4, _5, _6, _7, _8> tail) { super(_1, tail); this._1 = _1; - _2 = tail._1(); - _3 = tail._2(); - _4 = tail._3(); - _5 = tail._4(); - _6 = tail._5(); - _7 = tail._6(); - _8 = tail._7(); + _2 = tail._1(); + _3 = tail._2(); + _4 = tail._3(); + _5 = tail._4(); + _6 = tail._5(); + _7 = tail._6(); + _8 = tail._7(); } /** @@ -75,10 +75,11 @@ public <_0> HCons<_0, Tuple8<_1, _2, _3, _4, _5, _6, _7, _8>> cons(_0 _0) { } /** - * Snoc an element onto the back of this HList. + * Snoc an element onto the back of this {@link Tuple8}. * * @param _9 the new last element - * @return the updated HList + * @param <_9> the new last element type + * @return the new {@link HCons consed} {@link Tuple8} */ public <_9> HCons<_1, Tuple8<_2, _3, _4, _5, _6, _7, _8, _9>> snoc(_9 _9) { return singletonHList(_9).cons(_8).cons(_7).cons(_6).cons(_5).cons(_4).cons(_3).cons(_2).cons(_1); diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/HListTest.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/HListTest.java index 09e9e44d0..3e3513a3a 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/HListTest.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/HListTest.java @@ -2,9 +2,15 @@ import org.junit.Test; -import static com.jnape.palatable.lambda.adt.hlist.HList.*; +import static com.jnape.palatable.lambda.adt.hlist.HList.cons; +import static com.jnape.palatable.lambda.adt.hlist.HList.nil; +import static com.jnape.palatable.lambda.adt.hlist.HList.singletonHList; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static org.hamcrest.CoreMatchers.instanceOf; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; public class HListTest { @@ -48,16 +54,15 @@ public void nilReusesInstance() { } @Test - @SuppressWarnings({"EqualsWithItself", "EqualsBetweenInconvertibleTypes"}) public void equality() { - assertTrue(nil().equals(nil())); - assertTrue(cons(1, nil()).equals(cons(1, nil()))); + assertEquals(nil(), nil()); + assertEquals(cons(1, nil()), cons(1, nil())); - assertFalse(cons(1, nil()).equals(nil())); - assertFalse(nil().equals(cons(1, nil()))); + assertNotEquals(cons(1, nil()), nil()); + assertNotEquals(nil(), cons(1, nil())); - assertFalse(cons(1, cons(2, nil())).equals(cons(1, nil()))); - assertFalse(cons(1, nil()).equals(cons(1, cons(2, nil())))); + assertNotEquals(cons(1, cons(2, nil())), cons(1, nil())); + assertNotEquals(cons(1, nil()), cons(1, cons(2, nil()))); } @Test @@ -68,10 +73,4 @@ public void hashCodeUsesDecentDistribution() { assertNotEquals(nil().cons(1).hashCode(), nil().cons(2).hashCode()); assertNotEquals(nil().cons(1).cons(2).hashCode(), nil().cons(1).cons(3).hashCode()); } - - @Test - public void snoc() { - SingletonHList tuple = nil().snoc((float) 4.0); - assertEquals(4.0, tuple.head(), 0.01); - } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/SingletonHListTest.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/SingletonHListTest.java index 6d7d8c0b2..7a577f2ce 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/SingletonHListTest.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/SingletonHListTest.java @@ -47,6 +47,11 @@ public void cons() { assertEquals(new Tuple2<>("0", singletonHList), singletonHList.cons("0")); } + @Test + public void snoc() { + assertEquals(tuple((byte) 127, 'x'), singletonHList((byte) 127).snoc('x')); + } + @Test public void intoAppliesHeadToFn() { assertEquals("FOO", singletonHList("foo").into(String::toUpperCase)); @@ -57,10 +62,4 @@ public void staticPure() { SingletonHList singletonHList = pureSingletonHList().apply(1); assertEquals(singletonHList(1), singletonHList); } - - @Test - public void snoc() { - Tuple2 tuple = singletonHList((byte) 127).snoc('x'); - assertEquals(tuple((byte) 127, 'x'), tuple); - } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java index ce28b964b..31dc2f285 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java @@ -68,6 +68,11 @@ public void cons() { assertEquals(new Tuple3<>(0, tuple2), tuple2.cons(0)); } + @Test + public void snoc() { + assertEquals(tuple(Long.MAX_VALUE, 123, "hi"), tuple(Long.MAX_VALUE, 123).snoc("hi")); + } + @Test public void accessors() { assertEquals((Integer) 1, tuple2._1()); @@ -143,10 +148,4 @@ public void staticPure() { Tuple2 tuple = pureTuple(1).apply("two"); assertEquals(tuple(1, "two"), tuple); } - - @Test - public void snoc() { - Tuple3 tuple = tuple(Long.MAX_VALUE, 123).snoc("hi"); - assertEquals(tuple(Long.MAX_VALUE, 123, "hi"), tuple); - } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java index aa25c3e59..55842ee8b 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java @@ -13,8 +13,6 @@ import testsupport.traits.MonadRecLaws; import testsupport.traits.TraversableLaws; -import java.time.Duration; - import static com.jnape.palatable.lambda.adt.Maybe.just; import static com.jnape.palatable.lambda.adt.Maybe.nothing; import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; @@ -65,6 +63,12 @@ public void cons() { assertEquals(new Tuple4<>(0, tuple3), tuple3.cons(0)); } + @Test + public void snoc() { + assertEquals(tuple("qux", Long.MIN_VALUE, 7, ofSeconds(13)), + tuple("qux", Long.MIN_VALUE, 7).snoc(ofSeconds(13))); + } + @Test public void accessors() { assertEquals((Integer) 1, tuple3._1()); @@ -122,10 +126,4 @@ public void staticPure() { Tuple3 tuple = pureTuple(1, "2").apply('3'); assertEquals(tuple(1, "2", '3'), tuple); } - - @Test - public void snoc() { - Tuple4 tuple = tuple("qux", Long.MIN_VALUE, 7).snoc(ofSeconds(13)); - assertEquals(tuple("qux", Long.MIN_VALUE, 7, ofSeconds(13)), tuple); - } } diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java index a0b27f05e..bb5380891 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java @@ -62,6 +62,11 @@ public void cons() { assertEquals(new Tuple5<>(0, tuple4), tuple4.cons(0)); } + @Test + public void snoc() { + assertEquals(tuple("qux", 7, "foo", 13L, 17), tuple("qux", 7, "foo", 13L).snoc(17)); + } + @Test public void accessors() { assertEquals((Integer) 1, tuple4._1()); @@ -122,10 +127,4 @@ public void staticPure() { Tuple4 tuple = pureTuple(1, "2", '3').apply(true); assertEquals(tuple(1, "2", '3', true), tuple); } - - @Test - public void snoc() { - Tuple5 tuple = tuple("qux", 7, "foo", 13L).snoc(17); - assertEquals(tuple("qux", 7, "foo", 13L, 17), tuple); - } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java index ee0d6b7be..f156f9072 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java @@ -63,6 +63,11 @@ public void cons() { assertEquals(new HCons<>(0, tuple5), tuple5.cons(0)); } + @Test + public void snoc() { + assertEquals(tuple("a", 5, "b", 7, "c", 11), tuple("a", 5, "b", 7, "c").snoc(11)); + } + @Test public void accessors() { assertEquals((Integer) 1, tuple5._1()); @@ -128,10 +133,4 @@ public void staticPure() { Tuple5 tuple = pureTuple(1, "2", '3', true).apply(5f); assertEquals(tuple(1, "2", '3', true, 5f), tuple); } - - @Test - public void snoc() { - Tuple6 tuple = tuple("a", 5, "b", 7, "c").snoc(11); - assertEquals(tuple("a", 5, "b", 7, "c", 11), tuple); - } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java index dd57fde9c..a148ae972 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java @@ -64,6 +64,11 @@ public void cons() { assertEquals(new HCons<>(0, tuple6), tuple6.cons(0)); } + @Test + public void snoc() { + assertEquals(tuple(5L, "a", 7, "b", 11, "c", 13), tuple(5L, "a", 7, "b", 11, "c").snoc(13)); + } + @Test public void accessors() { assertEquals((Float) 2.0f, tuple6._1()); @@ -132,10 +137,4 @@ public void staticPure() { Tuple6 tuple = pureTuple(1, "2", '3', true, 5f).apply((byte) 6); assertEquals(tuple(1, "2", '3', true, 5f, (byte) 6), tuple); } - - @Test - public void snoc() { - Tuple7 tuple = tuple(5L, "a", 7, "b", 11, "c").snoc(13); - assertEquals(tuple(5L, "a", 7, "b", 11, "c", 13), tuple); - } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java index 14ccb30a6..f1cbb7f3a 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java @@ -64,6 +64,11 @@ public void cons() { assertEquals(new HCons<>(0, tuple7), tuple7.cons(0)); } + @Test + public void snoc() { + assertEquals(tuple("b", 7L, "c", 11, "d", 13, "e", 'f'), tuple("b", 7L, "c", 11, "d", 13, "e").snoc('f')); + } + @Test public void accessors() { assertEquals((Byte) (byte) 127, tuple7._1()); @@ -136,10 +141,4 @@ public void staticPure() { pureTuple((byte) 1, (short) 2, 3, 4L, 5F, 6D).apply(true); assertEquals(tuple((byte) 1, (short) 2, 3, 4L, 5F, 6D, true), tuple); } - - @Test - public void snoc() { - Tuple8 tuple = tuple("b", 7L, "c", 11, "d", 13, "e").snoc('f'); - assertEquals(tuple("b", 7L, "c", 11, "d", 13, "e", 'f'), tuple); - } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java index 33921079e..dacab1869 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java @@ -69,6 +69,15 @@ public void cons() { assertEquals(new HCons<>(0, tuple8), tuple8.cons(0)); } + @Test + public void snoc() { + LocalDate last = LocalDate.of(2020, 4, 14); + HCons> actual = + tuple("b", 7L, "c", 11, "d", 13, "e", 15L).snoc(last); + assertEquals("b", actual.head()); + assertEquals(actual.tail(), tuple(7L, "c", 11, "d", 13, "e", 15L, last)); + } + @Test public void accessors() { assertEquals((Short) (short) 65535, tuple8._1()); @@ -149,11 +158,4 @@ public void staticPure() { pureTuple((byte) 1, (short) 2, 3, 4L, 5F, 6D, true).apply('8'); assertEquals(tuple((byte) 1, (short) 2, 3, 4L, 5F, 6D, true, '8'), tuple); } - - @Test - public void snoc() { - HCons> actual = tuple("b", 7L, "c", 11, "d", 13, "e", 15L).snoc(LocalDate.of(2020, 4, 14)); - assertEquals("b", actual.head()); - assertEquals(actual.tail(), tuple(7L, "c", 11, "d", 13, "e", 15L, LocalDate.of(2020, 4, 14))); - } } \ No newline at end of file From 671a61d373a200de6bd0ef0565002bd6d86cdbc2 Mon Sep 17 00:00:00 2001 From: Michael A Date: Mon, 5 Oct 2020 17:46:01 -0500 Subject: [PATCH 13/49] #96 Add WuWei to Community Section --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 312d220fb..b7b46b83f 100644 --- a/README.md +++ b/README.md @@ -746,6 +746,7 @@ There are some open-sourced community projects that depend on _lambda_ for their - [Enhanced Iterables](https://github.com/kschuetz/enhanced-iterables) - Kevin Schuetz [@kschuetz](https://github.com/kschuetz) - [Collection Views](https://github.com/kschuetz/collection-views) - Kevin Schuetz [@kschuetz](https://github.com/kschuetz) +- [WuWei](https://github.com/nomicflux/WuWei) - Michael Anderson [@nomicflux](https://github.com/nomicflux) - `ST` monad for safe mutability License ------- From 3f23529573f7a2f7f7a11aca3d9d7e720cf2a15b Mon Sep 17 00:00:00 2001 From: Kevin Schuetz Date: Fri, 30 Oct 2020 18:13:44 -0500 Subject: [PATCH 14/49] #98 Add Kraftwerk to Community section --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b7b46b83f..eee42045c 100644 --- a/README.md +++ b/README.md @@ -747,6 +747,7 @@ There are some open-sourced community projects that depend on _lambda_ for their - [Enhanced Iterables](https://github.com/kschuetz/enhanced-iterables) - Kevin Schuetz [@kschuetz](https://github.com/kschuetz) - [Collection Views](https://github.com/kschuetz/collection-views) - Kevin Schuetz [@kschuetz](https://github.com/kschuetz) - [WuWei](https://github.com/nomicflux/WuWei) - Michael Anderson [@nomicflux](https://github.com/nomicflux) - `ST` monad for safe mutability +- [Kraftwerk](https://github.com/kschuetz/kraftwerk) - Kevin Schuetz [@kschuetz](https://github.com/kschuetz) - random data generators and combinators License ------- From d3a049c8df1ba7977b7e50253320499c0094ca8e Mon Sep 17 00:00:00 2001 From: jnape Date: Mon, 5 Oct 2020 17:24:39 -0500 Subject: [PATCH 15/49] ReaderT#fmap and StateT#fmap don't unnecessarily rely on pure --- CHANGELOG.md | 1 + .../monad/transformer/builtin/ReaderT.java | 2 +- .../monad/transformer/builtin/StateT.java | 4 ++-- .../monad/transformer/builtin/ReaderTTest.java | 15 +++++++++++++++ .../monad/transformer/builtin/StateTTest.java | 17 +++++++++++++++++ 5 files changed, 36 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e96c8697d..63edf0d5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ### Changed - `IterateT#unfold` now only computes a single `Pure` for the given input +- `ReaderT#fmap` and `StateT#fmap` avoid unnecessary calls to `pure` ### Added - `$`, function application represented as a higher-order `Fn2` diff --git a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/ReaderT.java b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/ReaderT.java index 443470c6e..e7f93a168 100644 --- a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/ReaderT.java +++ b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/ReaderT.java @@ -120,7 +120,7 @@ public ReaderT pure(B b) { */ @Override public ReaderT fmap(Fn1 fn) { - return MonadT.super.fmap(fn).coerce(); + return readerT(r -> runReaderT(r).fmap(fn)); } /** diff --git a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/StateT.java b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/StateT.java index 071ce0dc5..219ad3e03 100644 --- a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/StateT.java +++ b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/StateT.java @@ -144,7 +144,7 @@ public StateT pure(B b) { */ @Override public StateT fmap(Fn1 fn) { - return MonadT.super.fmap(fn).coerce(); + return stateT(s -> runStateT(s).fmap(t -> t.biMapL(fn))); } /** @@ -292,7 +292,7 @@ public static , A> StateT stateT( public static > Pure> pureStateT(Pure pureM) { return new Pure>() { @Override - public StateT checkedApply(A a) throws Throwable { + public StateT checkedApply(A a) { return stateT(pureM.>apply(a)); } }; diff --git a/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/ReaderTTest.java b/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/ReaderTTest.java index 6972c9fb6..cbcf1ef8b 100644 --- a/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/ReaderTTest.java +++ b/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/ReaderTTest.java @@ -2,6 +2,7 @@ import com.jnape.palatable.lambda.adt.Maybe; import com.jnape.palatable.lambda.adt.Unit; +import com.jnape.palatable.lambda.functions.Fn1; import com.jnape.palatable.lambda.functor.builtin.Identity; import com.jnape.palatable.lambda.io.IO; import com.jnape.palatable.traitor.annotations.TestTraits; @@ -12,6 +13,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; import static com.jnape.palatable.lambda.adt.Maybe.just; import static com.jnape.palatable.lambda.adt.Unit.UNIT; @@ -82,4 +84,17 @@ public void composedZip() { .unsafePerformAsyncIO(Executors.newFixedThreadPool(2)) .join(); } + + @Test + public void fmapInteractions() { + AtomicInteger invocations = new AtomicInteger(0); + ReaderT, Integer> readerT = readerT(i -> { + invocations.incrementAndGet(); + return new Identity<>(i); + }); + + Fn1 plusOne = x -> x + 1; + readerT.fmap(plusOne).fmap(plusOne).fmap(plusOne).runReaderT(0); + assertEquals(1, invocations.get()); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/StateTTest.java b/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/StateTTest.java index b51888c9d..785165952 100644 --- a/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/StateTTest.java +++ b/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/StateTTest.java @@ -16,15 +16,18 @@ import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; import static com.jnape.palatable.lambda.adt.Maybe.just; import static com.jnape.palatable.lambda.adt.Unit.UNIT; import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; import static com.jnape.palatable.lambda.functor.builtin.Identity.pureIdentity; import static com.jnape.palatable.lambda.optics.functions.Set.set; import static com.jnape.palatable.lambda.optics.lenses.ListLens.elementAt; import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; import static testsupport.matchers.StateTMatcher.whenEvaluated; import static testsupport.matchers.StateTMatcher.whenExecuted; import static testsupport.matchers.StateTMatcher.whenRun; @@ -119,4 +122,18 @@ public void staticLift() { assertThat(StateT.liftStateT().apply(new Identity<>(1)), whenRun("foo", new Identity<>(tuple(1, "foo")))); } + + @Test + public void fmapInteractions() { + AtomicInteger invocations = new AtomicInteger(0); + StateT., Integer>gets(x -> { + invocations.incrementAndGet(); + return new Identity<>(x); + }) + .fmap(id()) + .fmap(id()) + .fmap(id()) + .>>runStateT(0); + assertEquals(1, invocations.get()); + } } \ No newline at end of file From cc0cda9b6a6997e6e66a5511e8edb13fd8adde89 Mon Sep 17 00:00:00 2001 From: jnape Date: Wed, 7 Oct 2020 12:23:33 -0500 Subject: [PATCH 16/49] MaybeT implements MonadError --- CHANGELOG.md | 1 + .../monad/transformer/builtin/MaybeT.java | 24 ++++++++++++++++++- .../monad/transformer/builtin/MaybeTTest.java | 13 +++++++++- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63edf0d5d..8cedd4a86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ### Changed - `IterateT#unfold` now only computes a single `Pure` for the given input - `ReaderT#fmap` and `StateT#fmap` avoid unnecessary calls to `pure` +- `MaybeT` implements `MonadError` ### Added - `$`, function application represented as a higher-order `Fn2` diff --git a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/MaybeT.java b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/MaybeT.java index c2e02fd94..95dcdfd21 100644 --- a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/MaybeT.java +++ b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/MaybeT.java @@ -1,6 +1,7 @@ package com.jnape.palatable.lambda.monad.transformer.builtin; import com.jnape.palatable.lambda.adt.Maybe; +import com.jnape.palatable.lambda.adt.Unit; import com.jnape.palatable.lambda.functions.Fn1; import com.jnape.palatable.lambda.functions.recursion.RecursiveResult; import com.jnape.palatable.lambda.functions.specialized.Lift; @@ -9,6 +10,7 @@ import com.jnape.palatable.lambda.functor.builtin.Compose; import com.jnape.palatable.lambda.functor.builtin.Lazy; import com.jnape.palatable.lambda.monad.Monad; +import com.jnape.palatable.lambda.monad.MonadError; import com.jnape.palatable.lambda.monad.MonadRec; import com.jnape.palatable.lambda.monad.transformer.MonadT; @@ -16,6 +18,8 @@ import static com.jnape.palatable.lambda.adt.Maybe.just; import static com.jnape.palatable.lambda.adt.Maybe.nothing; +import static com.jnape.palatable.lambda.adt.Unit.UNIT; +import static com.jnape.palatable.lambda.functions.Fn0.fn0; import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly; import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.terminate; @@ -26,7 +30,7 @@ * @param the carrier type */ public final class MaybeT, A> implements - MonadT, MaybeT> { + MonadT, MaybeT>, MonadError> { private final MonadRec, M> mma; @@ -67,6 +71,24 @@ public MaybeT or(MaybeT other) { a -> mMaybeA.pure(just(a))))); } + /** + * {@inheritDoc} + */ + @Override + public MaybeT throwError(Unit unit) { + return maybeT(mma.pure(nothing())); + } + + /** + * {@inheritDoc} + */ + @Override + public MaybeT catchError(Fn1>> recoveryFn) { + return maybeT(mma.flatMap(maybeA -> maybeA.match( + fn0(() -> recoveryFn.apply(UNIT).>coerce().runMaybeT()), + a -> mma.pure(just(a))))); + } + /** * {@inheritDoc} */ diff --git a/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/MaybeTTest.java b/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/MaybeTTest.java index 2c578fbc6..1a81e1c57 100644 --- a/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/MaybeTTest.java +++ b/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/MaybeTTest.java @@ -22,14 +22,18 @@ import static com.jnape.palatable.lambda.adt.Either.right; import static com.jnape.palatable.lambda.adt.Maybe.just; import static com.jnape.palatable.lambda.adt.Maybe.nothing; +import static com.jnape.palatable.lambda.adt.Unit.UNIT; import static com.jnape.palatable.lambda.functions.builtin.fn2.GT.gt; import static com.jnape.palatable.lambda.functions.builtin.fn2.LT.lt; import static com.jnape.palatable.lambda.functor.builtin.Identity.pureIdentity; import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy; import static com.jnape.palatable.lambda.io.IO.io; -import static com.jnape.palatable.lambda.monad.transformer.builtin.MaybeT.*; +import static com.jnape.palatable.lambda.monad.transformer.builtin.MaybeT.liftMaybeT; +import static com.jnape.palatable.lambda.monad.transformer.builtin.MaybeT.maybeT; +import static com.jnape.palatable.lambda.monad.transformer.builtin.MaybeT.pureMaybeT; import static com.jnape.palatable.traitor.framework.Subjects.subjects; import static org.junit.Assert.assertEquals; +import static testsupport.assertion.MonadErrorAssert.assertLaws; @RunWith(Traits.class) public class MaybeTTest { @@ -41,6 +45,13 @@ public class MaybeTTest { maybeT(left("foo"))); } + @Test + public void monadError() { + assertLaws(subjects(maybeT(new Identity<>(nothing())), maybeT(new Identity<>(just(1)))), + UNIT, + e -> maybeT(new Identity<>(just(2)))); + } + @Test public void lazyZip() { assertEquals(maybeT(right(just(2))), From db1cbb7e5be587704421a52ffe5d6de22cc35ad9 Mon Sep 17 00:00:00 2001 From: Alexander Bandukwala <7h3kk1d@gmail.com> Date: Thu, 1 Oct 2020 20:17:59 -0500 Subject: [PATCH 17/49] Add init methods to Tuple classes --- CHANGELOG.md | 1 + .../com/jnape/palatable/lambda/adt/hlist/Tuple2.java | 9 +++++++++ .../com/jnape/palatable/lambda/adt/hlist/Tuple3.java | 10 ++++++++++ .../com/jnape/palatable/lambda/adt/hlist/Tuple4.java | 10 ++++++++++ .../com/jnape/palatable/lambda/adt/hlist/Tuple5.java | 10 ++++++++++ .../com/jnape/palatable/lambda/adt/hlist/Tuple6.java | 10 ++++++++++ .../com/jnape/palatable/lambda/adt/hlist/Tuple7.java | 10 ++++++++++ .../com/jnape/palatable/lambda/adt/hlist/Tuple8.java | 10 ++++++++++ .../jnape/palatable/lambda/adt/hlist/Tuple2Test.java | 5 +++++ .../jnape/palatable/lambda/adt/hlist/Tuple3Test.java | 6 ++++++ .../jnape/palatable/lambda/adt/hlist/Tuple4Test.java | 6 ++++++ .../jnape/palatable/lambda/adt/hlist/Tuple5Test.java | 6 ++++++ .../jnape/palatable/lambda/adt/hlist/Tuple6Test.java | 6 ++++++ .../jnape/palatable/lambda/adt/hlist/Tuple7Test.java | 6 ++++++ .../jnape/palatable/lambda/adt/hlist/Tuple8Test.java | 6 ++++++ 15 files changed, 111 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cedd4a86..4c3add484 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `IterateT#unfold` now only computes a single `Pure` for the given input - `ReaderT#fmap` and `StateT#fmap` avoid unnecessary calls to `pure` - `MaybeT` implements `MonadError` +- `Tuple2-8#init`, for populating a `TupleN` with all but the last element ### Added - `$`, function application represented as a higher-order `Fn2` diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java index cdf79be12..aafa40b5e 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java @@ -237,6 +237,15 @@ AppTrav extends Applicative> AppTrav traverse( return fn.apply(_2).fmap(_2Prime -> fmap(constantly(_2Prime))).fmap(Applicative::coerce).coerce(); } + /** + * Returns a {@link SingletonHList}<_1> of the first element. + * + * @return The {@link SingletonHList}<_1> + */ + public SingletonHList<_1> init() { + return invert().tail(); + } + /** * Static factory method for creating Tuple2s from {@link java.util.Map.Entry}s. * diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.java index 6fe7710b3..b392ffe38 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.java @@ -219,6 +219,16 @@ AppTrav extends Applicative> AppTrav traverse( return fn.apply(_3).fmap(_3Prime -> fmap(constantly(_3Prime))).fmap(Applicative::coerce).coerce(); } + /** + * Returns a {@link Tuple2}<_1, _2> of all the elements of this + * {@link Tuple3}<_1, _2, _3> except the last. + * + * @return The {@link Tuple2}<_1, _2> representing all but the last element + */ + public Tuple2<_1, _2> init() { + return rotateR3().tail(); + } + /** * Given a value of type A, produced an instance of this tuple with each slot set to that value. * diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.java index ebd946548..d8dc42868 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.java @@ -246,6 +246,16 @@ AppTrav extends Applicative> AppTrav traverse( return fn.apply(_4).fmap(_4Prime -> fmap(constantly(_4Prime))).fmap(Applicative::coerce).coerce(); } + /** + * Returns a {@link Tuple3}<_1, _2, _3> of all the elements of this + * {@link Tuple4}<_1, _2, _3, _4> except the last. + * + * @return The {@link Tuple3}<_1, _2, _3> representing all but the last element + */ + public Tuple3<_1, _2, _3> init() { + return rotateR4().tail(); + } + /** * Given a value of type A, produced an instance of this tuple with each slot set to that value. * diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java index 869a41f97..5a6f20cbb 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java @@ -273,6 +273,16 @@ AppTrav extends Applicative> AppTrav traverse( return fn.apply(_5).fmap(_3Prime -> fmap(constantly(_3Prime))).fmap(Applicative::coerce).coerce(); } + /** + * Returns a {@link Tuple4}<_1, _2, _3, _4> of all the elements of this + * {@link Tuple5}<_1, _2, _3, _4, _5> except the last. + * + * @return The {@link Tuple4}<_1, _2, _3, _4> representing all but the last element + */ + public Tuple4<_1, _2, _3, _4> init() { + return rotateR5().tail(); + } + /** * Given a value of type A, produced an instance of this tuple with each slot set to that value. * diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java index 72bbac9ba..1034e3b6b 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java @@ -304,6 +304,16 @@ AppTrav extends Applicative> AppTrav traverse( return fn.apply(_6).fmap(_6Prime -> fmap(constantly(_6Prime))).fmap(Applicative::coerce).coerce(); } + /** + * Returns a {@link Tuple5}<_1, _2, _3, _4, _5> of all the elements of this + * {@link Tuple6}<_1, _2, _3, _4, _5, _6> except the last. + * + * @return The {@link Tuple5}<_1, _2, _3, _4, _5> representing all but the last element + */ + public Tuple5<_1, _2, _3, _4, _5> init() { + return rotateR6().tail(); + } + /** * Given a value of type A, produced an instance of this tuple with each slot set to that value. * diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java index 3a2f027b7..cdfe552d6 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java @@ -334,6 +334,16 @@ AppTrav extends Applicative> AppTrav traverse( return fn.apply(_7).fmap(_7Prime -> fmap(constantly(_7Prime))).fmap(Applicative::coerce).coerce(); } + /** + * Returns a {@link Tuple6}<_1, _2, _3, _4, _5, _6> of all the elements of this + * {@link Tuple7}<_1, _2, _3, _4, _5, _6, _7> except the last. + * + * @return The {@link Tuple6}<_1, _2, _3, _4, _5, _6> representing all but the last element + */ + public Tuple6<_1, _2, _3, _4, _5, _6> init() { + return rotateR7().tail(); + } + /** * Given a value of type A, produced an instance of this tuple with each slot set to that value. * diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java index c97cea46d..079a30611 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java @@ -364,6 +364,16 @@ AppTrav extends Applicative> AppTrav traverse( return fn.apply(_8).fmap(_8Prime -> fmap(constantly(_8Prime))).fmap(Applicative::coerce).coerce(); } + /** + * Returns a {@link Tuple7}<_1, _2, _3, _4, _5, _6, _7> of all the elements of this + * {@link Tuple8}<_1, _2, _3, _4, _5, _6, _7, _8> except the last. + * + * @return The {@link Tuple7}<_1, _2, _3, _4, _5, _6, _7> representing all but the last element + */ + public Tuple7<_1, _2, _3, _4, _5, _6, _7> init() { + return rotateR8().tail(); + } + /** * Given a value of type A, produced an instance of this tuple with each slot set to that value. * diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java index 31dc2f285..6637386ce 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java @@ -63,6 +63,11 @@ public void tail() { assertEquals(new SingletonHList<>(2), tuple2.tail()); } + @Test + public void init() { + assertEquals(new SingletonHList<>(1), tuple2.init()); + } + @Test public void cons() { assertEquals(new Tuple3<>(0, tuple2), tuple2.cons(0)); diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java index 55842ee8b..a4cad8198 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java @@ -126,4 +126,10 @@ public void staticPure() { Tuple3 tuple = pureTuple(1, "2").apply('3'); assertEquals(tuple(1, "2", '3'), tuple); } + + @Test + public void init() { + assertEquals(tuple(1, 2), + tuple(1, 2, 3).init()); + } } diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java index bb5380891..13d5da838 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java @@ -127,4 +127,10 @@ public void staticPure() { Tuple4 tuple = pureTuple(1, "2", '3').apply(true); assertEquals(tuple(1, "2", '3', true), tuple); } + + @Test + public void init() { + assertEquals(tuple(1, 2, 3), + tuple(1, 2, 3, 4).init()); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java index f156f9072..7b81a2190 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java @@ -133,4 +133,10 @@ public void staticPure() { Tuple5 tuple = pureTuple(1, "2", '3', true).apply(5f); assertEquals(tuple(1, "2", '3', true, 5f), tuple); } + + @Test + public void init() { + assertEquals(tuple(1, 2, 3, 4), + tuple(1, 2, 3, 4, 5).init()); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java index a148ae972..880eee1e4 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java @@ -137,4 +137,10 @@ public void staticPure() { Tuple6 tuple = pureTuple(1, "2", '3', true, 5f).apply((byte) 6); assertEquals(tuple(1, "2", '3', true, 5f, (byte) 6), tuple); } + + @Test + public void init() { + assertEquals(tuple(1, 2, 3, 4, 5), + tuple(1, 2, 3, 4, 5, 6).init()); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java index f1cbb7f3a..d5b13fd24 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java @@ -141,4 +141,10 @@ public void staticPure() { pureTuple((byte) 1, (short) 2, 3, 4L, 5F, 6D).apply(true); assertEquals(tuple((byte) 1, (short) 2, 3, 4L, 5F, 6D, true), tuple); } + + @Test + public void init() { + assertEquals(tuple(1, 2, 3, 4, 5, 6), + tuple(1, 2, 3, 4, 5, 6, 7).init()); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java index dacab1869..ced5531b5 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java @@ -158,4 +158,10 @@ public void staticPure() { pureTuple((byte) 1, (short) 2, 3, 4L, 5F, 6D, true).apply('8'); assertEquals(tuple((byte) 1, (short) 2, 3, 4L, 5F, 6D, true, '8'), tuple); } + + @Test + public void init() { + assertEquals(tuple(1, 2, 3, 4, 5, 6, 7), + tuple(1, 2, 3, 4, 5, 6, 7, 8).init()); + } } \ No newline at end of file From a659fc441b37ae77b9c93f7e5177b35bcd7683ea Mon Sep 17 00:00:00 2001 From: Nate Riffe Date: Fri, 16 Oct 2020 22:37:10 -0500 Subject: [PATCH 18/49] Simplify internal structure of IterateT - Reduce representation of the spine to a single ImmutableQueue containing Choice2s of thunks or realized nodes - Conses and snocs occupy the b "real" side of the coproduct and are pushed to the front or back of the spine, respectively - IterateTs which would have been middles in the old representation are now just another spine to concat onto and are never actually instantiated as IterateTs - Handling of thunks is essentially unchanged - Delegate the iterateT static constructor to suspended and the singleton static constructor to empty with a cons for clarity and concision Fix up formatting - Break the long return type of resume between the Fn1 type arguments - Undo some unintentional reformatting of fromIterator --- .../monad/transformer/builtin/IterateT.java | 90 ++++++------------- 1 file changed, 27 insertions(+), 63 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java index 07ea19982..b258cb983 100644 --- a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java +++ b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java @@ -68,18 +68,12 @@ public class IterateT, A> implements MonadT, IterateT> { private final Pure pureM; - private final ImmutableQueue> conses; - private final ImmutableQueue>>, M>>, IterateT>> middles; - private final ImmutableQueue> snocs; + private final ImmutableQueue>>, M>>, MonadRec>> spine; private IterateT(Pure pureM, - ImmutableQueue> conses, - ImmutableQueue>>, M>>, IterateT>> middles, - ImmutableQueue> snocs) { - this.pureM = pureM; - this.conses = conses; - this.middles = middles; - this.snocs = snocs; + ImmutableQueue>>, M>>, MonadRec>> spine) { + this.pureM = pureM; + this.spine = spine; } /** @@ -89,7 +83,9 @@ private IterateT(Pure pureM, * @return the embedded {@link Monad} */ public >>, M>> MMTA runIterateT() { - return pureM., MonadRec, M>>apply(this).trampolineM(IterateT::resume).coerce(); + MonadRec>>, M>>, MonadRec>>, M> + mSpine = pureM.apply(spine); + return mSpine.trampolineM(resume(pureM)).coerce(); } /** @@ -99,7 +95,7 @@ public >>, M>> MMTA runIter * @return the cons'ed {@link IterateT} */ public final IterateT cons(MonadRec head) { - return new IterateT<>(pureM, conses.pushFront(head), middles, snocs); + return new IterateT<>(pureM, spine.pushFront(b(head))); } /** @@ -109,7 +105,7 @@ public final IterateT cons(MonadRec head) { * @return the snoc'ed {@link IterateT} */ public final IterateT snoc(MonadRec last) { - return new IterateT<>(pureM, conses, middles, snocs.pushBack(last)); + return new IterateT<>(pureM, spine.pushBack(b(last))); } /** @@ -119,13 +115,7 @@ public final IterateT snoc(MonadRec last) { * @return the concatenated {@link IterateT} */ public IterateT concat(IterateT other) { - return new IterateT<>(pureM, - conses, - middles.pushBack(b(new IterateT<>(pureM, - snocs.concat(other.conses), - other.middles, - other.snocs))), - ImmutableQueue.empty()); + return new IterateT<>(pureM, spine.concat(other.spine)); } /** @@ -289,34 +279,17 @@ public IterateT discardR(Applicative> appB) { return MonadT.super.discardR(appB).coerce(); } - private MonadRec, Maybe>>>, M> resume() { - return conses.head().match( - __ -> middles.head().match( - ___ -> snocs.head().match( - ____ -> pureM.apply(terminate(nothing())), - ma -> ma.fmap(a -> terminate(just(tuple(a, new IterateT<>(pureM, - snocs.tail(), - ImmutableQueue.empty(), - ImmutableQueue.empty())))))), - lazyOrStrict -> lazyOrStrict.match( - lazy -> lazy.apply().fmap(maybeRes -> maybeRes.match( - ___ -> recurse(new IterateT<>(pureM, ImmutableQueue.empty(), middles.tail(), snocs)), - into((a, as) -> recurse(new IterateT<>(pureM, - ImmutableQueue.singleton(pureM.apply(a)), - ImmutableQueue.singleton(b(migrateForward(as))), - ImmutableQueue.empty()))) - )), - strict -> pureM.apply(recurse(migrateForward(strict))))), - ma -> ma.fmap(a -> terminate(just(tuple(a, new IterateT<>(pureM, conses.tail(), middles, snocs)))))); - } - - private IterateT migrateForward(IterateT as) { - if (middles.tail().isEmpty()) { - return new IterateT<>(pureM, conses.concat(as.conses), as.middles, as.snocs.concat(snocs)); - } - - IterateT lasts = new IterateT<>(pureM, as.snocs, middles.tail(), snocs); - return new IterateT<>(pureM, as.conses, as.middles.pushBack(b(lasts)), ImmutableQueue.empty()); + private static , A> + Fn1>>, M>>, MonadRec>>, + MonadRec>>, M>>, MonadRec>>, Maybe>>>, M>> + resume(Pure pureM) { + return spine -> spine.head().match( + ___ -> pureM.apply(terminate(nothing())), + thunkOrReal -> thunkOrReal.match( + thunk -> thunk.apply().fmap(m -> m.match( + ___ -> recurse(spine.tail()), + t -> terminate(just(t.fmap(as -> new IterateT<>(pureM, as.spine.concat(spine.tail()))))))), + real -> real.fmap(a -> terminate(just(tuple(a, new IterateT<>(pureM, spine.tail()))))))); } private IterateT trampolineM(Fn1, IterateT>> fn, @@ -346,7 +319,7 @@ private IterateT trampolineM(Fn1, A> IterateT empty(Pure pureM) { - return new IterateT<>(pureM, ImmutableQueue.empty(), ImmutableQueue.empty(), ImmutableQueue.empty()); + return new IterateT<>(pureM, ImmutableQueue.empty()); } /** @@ -358,10 +331,7 @@ public static , A> IterateT empty(Pure pureM) * @return the singleton {@link IterateT} */ public static , A> IterateT singleton(MonadRec ma) { - return new IterateT<>(Pure.of(ma), - ImmutableQueue.>empty().pushFront(ma), - ImmutableQueue.empty(), - ImmutableQueue.empty()); + return IterateT.empty(Pure.of(ma)).cons(ma); } /** @@ -374,12 +344,7 @@ public static , A> IterateT singleton(MonadRec, A> IterateT iterateT( MonadRec>>, M> unwrapped) { - return new IterateT<>( - Pure.of(unwrapped), - ImmutableQueue.empty(), - ImmutableQueue.>>, M>>, IterateT>>empty() - .pushFront(a(() -> unwrapped)), - ImmutableQueue.empty()); + return suspended(() -> unwrapped, Pure.of(unwrapped)); } /** @@ -434,11 +399,10 @@ public static , A, B> IterateT unfold( public static , A> IterateT suspended( Fn0>>, M>> thunk, Pure pureM) { return new IterateT<>(pureM, - ImmutableQueue.empty(), ImmutableQueue - .>>, M>>, IterateT>>empty() - .pushFront(a(thunk)), - ImmutableQueue.empty()); + .>>, M>>, MonadRec>>empty() + .pushFront(a(thunk)) + ); } /** From 8e059cc422606a85ad0bf1be3273c1d0edb0ee81 Mon Sep 17 00:00:00 2001 From: Nate Riffe Date: Tue, 10 Nov 2020 13:51:24 -0600 Subject: [PATCH 19/49] Failing test case for IterateT::trampolineM The existing implementation of trampolineM has a bug. The lambda on line 305 does not process `rest` leading to a premature termination of the overall trampoline operation. --- .../lambda/monad/transformer/builtin/IterateTTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java b/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java index b74e93835..4ff96ed78 100644 --- a/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java +++ b/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java @@ -247,4 +247,14 @@ public void staticLift() { ., IterateT, Integer>>apply(new Identity<>(1)) ., Identity>>toCollection(ArrayList::new)); } + + @Test + public void trampolineMRecursesBreadth() { + IterateT, Integer> firstFour = of(new Identity<>(1), new Identity<>(2), new Identity<>(3), new Identity<>(4)); + IterateT, Integer> trampolined = firstFour + .trampolineM(x -> (x % 3 == 0 && (x < 30)) + ? of(new Identity<>(terminate(x + 10)), new Identity<>(recurse(x + 11)), new Identity<>(recurse(x + 12)), new Identity<>(recurse(x + 13))) + : singleton(new Identity<>(terminate(x)))); + assertThat(trampolined, iterates(1, 2, 13, 14, 25, 26, 37, 38, 39, 40, 28, 16, 4)); + } } \ No newline at end of file From 8511223173d43736082c8baba4ce753269026acf Mon Sep 17 00:00:00 2001 From: Nate Riffe Date: Tue, 10 Nov 2020 14:01:17 -0600 Subject: [PATCH 20/49] Simplify and fix IterateT::trampolineM By changing the type of queue from `ImmutableQueue>>` to just `IterateT>`, there is no need to consider the total queue in two dimensions. This simplifies the suspended thunk down to the three fundamental cases that have to be handled: 1) The queue (which is now an `IterateT`) is empty: terminate(nothing()) 2) The first element is a recurse: map it to the concatenation of the processed A and the existing tail 3) The first element is a terminate: emit the node Additionally, the private arity-2 trampoline can be converted to a `withSelf` lambda to put the entire implementation in one place. --- .../monad/transformer/builtin/IterateT.java | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java index b258cb983..29196dd5d 100644 --- a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java +++ b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java @@ -181,9 +181,19 @@ public > IterateT lift(MonadRec nb) { * {@inheritDoc} */ @Override + @SuppressWarnings("RedundantTypeArguments") public IterateT trampolineM( Fn1, IterateT>> fn) { - return trampolineM(fn, ImmutableQueue.>>empty().pushBack(flatMap(fn))); + return $(withSelf( + (self, queued) -> suspended( + () -> pureM.>, MonadRec>, M>>apply(queued) + .trampolineM(q -> q.runIterateT().>, Maybe>>>>fmap(m -> m.match( + __ -> terminate(nothing()), + into((rr, tail) -> rr.biMap( + a -> fn.apply(a).>>coerce().concat(tail), + b -> just(tuple(b, self.apply(tail)))))))), + pureM)), + flatMap(fn)); } /** @@ -292,24 +302,6 @@ public IterateT discardR(Applicative> appB) { real -> real.fmap(a -> terminate(just(tuple(a, new IterateT<>(pureM, spine.tail()))))))); } - private IterateT trampolineM(Fn1, IterateT>> fn, - ImmutableQueue>> queued) { - return suspended(() -> { - MonadRec>>, M> pureQueue = pureM.apply(queued); - return pureQueue.trampolineM( - q -> q.head().match( - __ -> pureM.apply(terminate(nothing())), - next -> next.runIterateT().flatMap(maybeMore -> maybeMore.match( - __ -> pureM.apply(terminate(nothing())), - t -> t.into((aOrB, rest) -> aOrB.match( - a -> pureM.apply(recurse(q.pushFront(fn.apply(a).coerce()))), - b -> trampolineM(fn, q.tail().pushFront(rest)) - .cons(pureM.apply(b)) - .runIterateT() - .fmap(RecursiveResult::terminate))))))); - }, pureM); - } - /** * Static factory method for creating an empty {@link IterateT}. * From 7ac280ce806f87b968094ab118c5288dc4945c13 Mon Sep 17 00:00:00 2001 From: Nate Riffe Date: Tue, 10 Nov 2020 14:12:00 -0600 Subject: [PATCH 21/49] Simplify `of` and `suspended` static constructors - `of` can use a singleton as the seed instead of an empty and avoid consing a to as - `suspended` can construct a singleton queue instead of pushing onto an empty queue --- .../lambda/monad/transformer/builtin/IterateT.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java index 29196dd5d..f8e155aa1 100644 --- a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java +++ b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java @@ -353,9 +353,7 @@ public static , A> IterateT of( MonadRec ma, MonadRec... mas) { @SuppressWarnings("varargs") List> as = asList(mas); - return foldLeft(IterateT::snoc, - empty(Pure.of(ma)), - com.jnape.palatable.lambda.functions.builtin.fn2.Cons.cons(ma, as)); + return foldLeft(IterateT::snoc, singleton(ma), as); } /** @@ -390,11 +388,7 @@ public static , A, B> IterateT unfold( */ public static , A> IterateT suspended( Fn0>>, M>> thunk, Pure pureM) { - return new IterateT<>(pureM, - ImmutableQueue - .>>, M>>, MonadRec>>empty() - .pushFront(a(thunk)) - ); + return new IterateT<>(pureM, ImmutableQueue.singleton(a(thunk))); } /** From 9c57a344393b393c13a1f78c07e4f31bac439849 Mon Sep 17 00:00:00 2001 From: Nate Riffe Date: Tue, 10 Nov 2020 14:16:45 -0600 Subject: [PATCH 22/49] Inline resume Add explicit type arguments to the first match in the lambda returned by resume so that resume can be inlined, renaming the `spine` lambda parameter to `tSpine` for disambiguation --- .../monad/transformer/builtin/IterateT.java | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java index f8e155aa1..df49e428d 100644 --- a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java +++ b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java @@ -85,7 +85,13 @@ private IterateT(Pure pureM, public >>, M>> MMTA runIterateT() { MonadRec>>, M>>, MonadRec>>, M> mSpine = pureM.apply(spine); - return mSpine.trampolineM(resume(pureM)).coerce(); + return mSpine.trampolineM(tSpine -> tSpine.head().>>, M>>, MonadRec>>, Maybe>>>, M>>match( + ___ -> pureM.apply(terminate(nothing())), + thunkOrReal -> thunkOrReal.match( + thunk -> thunk.apply().fmap(m -> m.match( + ___ -> recurse(tSpine.tail()), + t -> terminate(just(t.fmap(as -> new IterateT<>(pureM, as.spine.concat(tSpine.tail()))))))), + real -> real.fmap(a -> terminate(just(tuple(a, new IterateT<>(pureM, tSpine.tail())))))))).coerce(); } /** @@ -289,19 +295,6 @@ public IterateT discardR(Applicative> appB) { return MonadT.super.discardR(appB).coerce(); } - private static , A> - Fn1>>, M>>, MonadRec>>, - MonadRec>>, M>>, MonadRec>>, Maybe>>>, M>> - resume(Pure pureM) { - return spine -> spine.head().match( - ___ -> pureM.apply(terminate(nothing())), - thunkOrReal -> thunkOrReal.match( - thunk -> thunk.apply().fmap(m -> m.match( - ___ -> recurse(spine.tail()), - t -> terminate(just(t.fmap(as -> new IterateT<>(pureM, as.spine.concat(spine.tail()))))))), - real -> real.fmap(a -> terminate(just(tuple(a, new IterateT<>(pureM, spine.tail()))))))); - } - /** * Static factory method for creating an empty {@link IterateT}. * From 2cdc1cf0425ffd89c2783483620dbe4473ee75f8 Mon Sep 17 00:00:00 2001 From: jnape Date: Wed, 11 Nov 2020 14:23:08 -0600 Subject: [PATCH 23/49] Updating CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c3add484..d75c39359 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `Fn1#withSelf`, a static method for constructing a self-referencing `Fn1` - `HNil/SingletonHList/TupleX#snoc`, a method to add a new last element (append to a tuple) +### Fixed +- `IterateT#trampolineM` now yields and stages all recursive result values, rather + than prematurely terminating on the first termination result + ## [5.2.0] - 2020-02-12 ### Changed From cda383d4aa525e95ce3e86e293a7e4517d1456b7 Mon Sep 17 00:00:00 2001 From: jnape Date: Fri, 27 Nov 2020 12:38:52 -0600 Subject: [PATCH 24/49] IterateT#flatMap is now stack-safe regardless of how many consecutive empty `IterateT`s are returned --- CHANGELOG.md | 2 + .../monad/transformer/builtin/IterateT.java | 17 +++-- .../transformer/builtin/IterateTTest.java | 72 ++++++++++++++++--- 3 files changed, 77 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d75c39359..1a1bf3b6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ### Fixed - `IterateT#trampolineM` now yields and stages all recursive result values, rather than prematurely terminating on the first termination result +- `IterateT#flatMap` is now stack-safe regardless of how many consecutive empty `IterateT`s + are returned and regardless of whether the monad is strict or lazy or internally trampolined ## [5.2.0] - 2020-02-12 diff --git a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java index df49e428d..d651795b5 100644 --- a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java +++ b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java @@ -29,6 +29,7 @@ import static com.jnape.palatable.lambda.adt.choice.Choice2.a; import static com.jnape.palatable.lambda.adt.choice.Choice2.b; import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.Fn0.fn0; import static com.jnape.palatable.lambda.functions.Fn1.withSelf; import static com.jnape.palatable.lambda.functions.builtin.fn2.$.$; import static com.jnape.palatable.lambda.functions.builtin.fn2.Into.into; @@ -65,7 +66,7 @@ * @param the element type */ public class IterateT, A> implements - MonadT, IterateT> { + MonadT, IterateT> { private final Pure pureM; private final ImmutableQueue>>, M>>, MonadRec>> spine; @@ -208,11 +209,15 @@ public IterateT trampolineM( @Override public IterateT flatMap(Fn1>> f) { return suspended(() -> maybeT(runIterateT()) - .flatMap(into((a, as) -> maybeT(f.apply(a) - .>coerce() - .concat(as.flatMap(f)) - .runIterateT()))) - .runMaybeT(), pureM); + .trampolineM(into((a, as) -> maybeT( + f.apply(a).>coerce().runIterateT() + .flatMap(maybePair -> maybePair.match( + fn0(() -> as.runIterateT() + .fmap(maybeResult -> maybeResult.fmap(RecursiveResult::recurse))), + t -> pureM.apply(just(terminate(t.fmap(mb -> mb.concat(as.flatMap(f)))))) + ))))) + .runMaybeT(), + pureM); } /** diff --git a/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java b/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java index 4ff96ed78..a0e974d43 100644 --- a/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java +++ b/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java @@ -8,31 +8,47 @@ import com.jnape.palatable.lambda.functor.builtin.Lazy; import com.jnape.palatable.lambda.functor.builtin.Writer; import com.jnape.palatable.lambda.io.IO; +import com.jnape.palatable.lambda.monoid.Monoid; import com.jnape.palatable.traitor.annotations.TestTraits; import com.jnape.palatable.traitor.framework.Subjects; import com.jnape.palatable.traitor.runners.Traits; import org.junit.Test; import org.junit.runner.RunWith; -import testsupport.traits.*; +import testsupport.traits.ApplicativeLaws; +import testsupport.traits.Equivalence; +import testsupport.traits.FunctorLaws; +import testsupport.traits.MonadLaws; +import testsupport.traits.MonadRecLaws; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; import static com.jnape.palatable.lambda.adt.Maybe.just; import static com.jnape.palatable.lambda.adt.Maybe.nothing; import static com.jnape.palatable.lambda.adt.Unit.UNIT; import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly; import static com.jnape.palatable.lambda.functions.builtin.fn2.LTE.lte; import static com.jnape.palatable.lambda.functions.builtin.fn3.Times.times; import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.recurse; import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.terminate; import static com.jnape.palatable.lambda.functor.builtin.Identity.pureIdentity; import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy; -import static com.jnape.palatable.lambda.functor.builtin.Writer.*; +import static com.jnape.palatable.lambda.functor.builtin.Writer.listen; +import static com.jnape.palatable.lambda.functor.builtin.Writer.pureWriter; +import static com.jnape.palatable.lambda.functor.builtin.Writer.tell; +import static com.jnape.palatable.lambda.functor.builtin.Writer.writer; import static com.jnape.palatable.lambda.io.IO.io; -import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.*; +import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.empty; +import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.iterateT; +import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.liftIterateT; +import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.of; +import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.pureIterateT; +import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.singleton; +import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.unfold; import static com.jnape.palatable.lambda.monoid.builtin.AddAll.addAll; import static com.jnape.palatable.lambda.monoid.builtin.Join.join; import static com.jnape.palatable.traitor.framework.Subjects.subjects; @@ -44,7 +60,9 @@ import static org.junit.Assert.assertThat; import static testsupport.Constants.STACK_EXPLODING_NUMBER; import static testsupport.matchers.IOMatcher.yieldsValue; -import static testsupport.matchers.IterateTMatcher.*; +import static testsupport.matchers.IterateTMatcher.isEmpty; +import static testsupport.matchers.IterateTMatcher.iterates; +import static testsupport.matchers.IterateTMatcher.iteratesAll; import static testsupport.traits.Equivalence.equivalence; @RunWith(Traits.class) @@ -236,16 +254,16 @@ public void concatIsStackSafe() { public void staticPure() { assertEquals(new Identity<>(singletonList(1)), pureIterateT(pureIdentity()) - ., Integer>>apply(1) - ., Identity>>toCollection(ArrayList::new)); + ., Integer>>apply(1) + ., Identity>>toCollection(ArrayList::new)); } @Test public void staticLift() { assertEquals(new Identity<>(singletonList(1)), liftIterateT() - ., IterateT, Integer>>apply(new Identity<>(1)) - ., Identity>>toCollection(ArrayList::new)); + ., IterateT, Integer>>apply(new Identity<>(1)) + ., Identity>>toCollection(ArrayList::new)); } @Test @@ -257,4 +275,42 @@ public void trampolineMRecursesBreadth() { : singleton(new Identity<>(terminate(x)))); assertThat(trampolined, iterates(1, 2, 13, 14, 25, 26, 37, 38, 39, 40, 28, 16, 4)); } + + @Test + public void flatMapToEmptyStackSafety() { + assertEquals(new Identity<>(UNIT), + unfold(x -> new Identity<>(x <= STACK_EXPLODING_NUMBER ? just(tuple(x, x + 1)) : nothing()), + new Identity<>(1)) + .flatMap(constantly(iterateT(new Identity<>(nothing())))) + .forEach(constantly(new Identity<>(UNIT)))); + + assertEquals((Integer) 1_250_025_000, + unfold(x -> listen(x <= STACK_EXPLODING_NUMBER ? just(tuple(x, x + 1)) : nothing()), + Writer.listen(1)) + .flatMap(x -> iterateT(writer(tuple(nothing(), x)))) + .>forEach(constantly(listen(UNIT))) + .runWriter(Monoid.monoid(Integer::sum, 0)) + ._2()); + } + + @Test + public void flatMapCostsNoMoreEffortThanRequiredToYieldFirstValue() { + AtomicInteger flatMapCost = new AtomicInteger(0); + AtomicInteger unfoldCost = new AtomicInteger(0); + assertEquals(just(1), + unfold(x -> { + unfoldCost.incrementAndGet(); + return new Identity<>(x <= 10 ? just(tuple(x, x + 1)) : nothing()); + }, + new Identity<>(1)) + .flatMap(x -> { + flatMapCost.incrementAndGet(); + return singleton(new Identity<>(x)); + }) + ., Integer>>>>>runIterateT() + .runIdentity() + .fmap(Tuple2::_1)); + assertEquals(1, flatMapCost.get()); + assertEquals(1, unfoldCost.get()); + } } \ No newline at end of file From d02c0bc9972b188f8f140c0c7dde0fe7ce9a737a Mon Sep 17 00:00:00 2001 From: jnape Date: Fri, 27 Nov 2020 17:13:13 -0600 Subject: [PATCH 25/49] [maven-release-plugin] prepare release lambda-5.3.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bda3d7caf..820393917 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ lambda - 5.2.1-SNAPSHOT + 5.3.0 jar Lambda From 256d3302a9b8a6eb6e5eee8cba2ab1ecf828ebd1 Mon Sep 17 00:00:00 2001 From: jnape Date: Fri, 27 Nov 2020 17:13:20 -0600 Subject: [PATCH 26/49] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 820393917..cc4cfc72d 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ lambda - 5.3.0 + 5.3.1-SNAPSHOT jar Lambda From ee4371e8626c703458b47cba57784d10922ffdbe Mon Sep 17 00:00:00 2001 From: jnape Date: Mon, 7 Dec 2020 10:20:23 -0600 Subject: [PATCH 27/49] Updating CHANGELOG.md --- CHANGELOG.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a1bf3b6b..9c95ef614 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,16 +5,20 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] +There are currently no unreleased changes. + +## [5.3.0] - 2020-12-07 + ### Changed - `IterateT#unfold` now only computes a single `Pure` for the given input - `ReaderT#fmap` and `StateT#fmap` avoid unnecessary calls to `pure` - `MaybeT` implements `MonadError` -- `Tuple2-8#init`, for populating a `TupleN` with all but the last element ### Added - `$`, function application represented as a higher-order `Fn2` -- `Fn1#withSelf`, a static method for constructing a self-referencing `Fn1` +- `Fn1#withSelf`, a static method for constructing a self-referencing `Fn1` - `HNil/SingletonHList/TupleX#snoc`, a method to add a new last element (append to a tuple) +- `Tuple2-8#init`, for populating a `TupleN` with all but the last element ### Fixed - `IterateT#trampolineM` now yields and stages all recursive result values, rather @@ -576,7 +580,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `Monadic/Dyadic/TriadicFunction`, `Predicate`, `Tuple2`, `Tuple3` - `Functor`, `BiFunctor`, `ProFunctor` -[Unreleased]: https://github.com/palatable/lambda/compare/lambda-5.2.0...HEAD +[Unreleased]: https://github.com/palatable/lambda/compare/lambda-5.3.0...HEAD +[5.3.0]: https://github.com/palatable/lambda/compare/lambda-5.2.0...lambda-5.3.0 [5.2.0]: https://github.com/palatable/lambda/compare/lambda-5.1.0...lambda-5.2.0 [5.1.0]: https://github.com/palatable/lambda/compare/lambda-5.0.0...lambda-5.1.0 [5.0.0]: https://github.com/palatable/lambda/compare/lambda-4.0.0...lambda-5.0.0 From 48ae7020641708ef9af7e00ef16a7b38c5742839 Mon Sep 17 00:00:00 2001 From: jnape Date: Mon, 7 Dec 2020 10:21:01 -0600 Subject: [PATCH 28/49] Updating README to reference latest version --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eee42045c..85790b22e 100644 --- a/README.md +++ b/README.md @@ -61,14 +61,14 @@ Add the following dependency to your: com.jnape.palatable lambda - 5.2.0 + 5.3.0 ``` `build.gradle` ([Gradle](https://docs.gradle.org/current/userguide/dependency_management.html)): ```gradle -compile group: 'com.jnape.palatable', name: 'lambda', version: '5.2.0' +compile group: 'com.jnape.palatable', name: 'lambda', version: '5.3.0' ``` Examples From a472c9f59b4b0825de2fa4b8098acb799cb1437a Mon Sep 17 00:00:00 2001 From: John Napier Date: Wed, 9 Dec 2020 15:00:29 -0600 Subject: [PATCH 29/49] Updating community section of README --- README.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 85790b22e..060d4e9ee 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Functional patterns for Java - [Either](#either) - [Lenses](#lenses) - [Notes](#notes) - - [Community](#community) + - [Ecosystem](#ecosystem) - [License](#license) Background @@ -740,9 +740,18 @@ Wherever possible, _lambda_ maintains interface compatibility with similar, fami Unfortunately, due to Java's type hierarchy and inheritance inconsistencies, this is not always possible. One surprising example of this is how `Fn1` extends `j.u.f.Function`, but `Fn2` does not extend `j.u.f.BiFunction`. This is because `j.u.f.BiFunction` itself does not extend `j.u.f.Function`, but it does define methods that collide with `j.u.f.Function`. For this reason, both `Fn1` and `Fn2` cannot extend their Java counterparts without sacrificing their own inheritance hierarchy. These types of asymmetries are, unfortunately, not uncommon; however, wherever these situations arise, measures are taken to attempt to ease the transition in and out of core Java types (in the case of `Fn2`, a supplemental `#toBiFunction` method is added). I do not take these inconveniences for granted, and I'm regularly looking for ways to minimize the negative impact of this as much as possible. Suggestions and use cases that highlight particular pain points here are particularly appreciated. -Community +Ecosystem ----- -There are some open-sourced community projects that depend on _lambda_ for their own functionality: these projects are listed below (note that these projects are _not_ affiliated with lambda, and have their own maintainers). If you use _lambda_ in your own open-sourced project, feel free to create an issue and I'll be happy to review the project and add it to this section! + +### Official extension libraries: + +These are officially supported libraries that extend lambda's core functionality and are developed under the same governance and processes as lambda. + +- [Shōki](https://github.com/palatable/shoki) - Purely functional, persistent data structures for the JVM + +### Third-party community libraries: + +These are open-sourced community projects that rely on _lambda_ for significant functionality, but are not necessarily affiliated with lambda and have their own separate maintainers. If you use _lambda_ in your own open-sourced project, feel free to create an issue and I'll be happy to review the project and add it to this section! - [Enhanced Iterables](https://github.com/kschuetz/enhanced-iterables) - Kevin Schuetz [@kschuetz](https://github.com/kschuetz) - [Collection Views](https://github.com/kschuetz/collection-views) - Kevin Schuetz [@kschuetz](https://github.com/kschuetz) From 86743e0978cdf200a5f7f009d9975c43729d8eef Mon Sep 17 00:00:00 2001 From: Nate Riffe Date: Sun, 6 Dec 2020 09:40:04 -0600 Subject: [PATCH 30/49] Loosen EitherMatcher types, add static constructors Add isRight() and isLeft() matcher constructors to EitherMatcher. To implement these using the anything() core matcher as the inner matcher, the type signature in isRightThat(..) and isLeftThat(..) has to be loosened since this and some other core matchers are not strictly typed with parameterized types. --- .../testsupport/matchers/EitherMatcher.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/test/java/testsupport/matchers/EitherMatcher.java b/src/test/java/testsupport/matchers/EitherMatcher.java index 748d01be2..2023a22b2 100644 --- a/src/test/java/testsupport/matchers/EitherMatcher.java +++ b/src/test/java/testsupport/matchers/EitherMatcher.java @@ -9,11 +9,12 @@ import static com.jnape.palatable.lambda.adt.Either.right; import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly; import static com.jnape.palatable.lambda.io.IO.io; +import static org.hamcrest.CoreMatchers.anything; public final class EitherMatcher extends TypeSafeMatcher> { - private final Either, Matcher> matcher; + private final Either, Matcher> matcher; - private EitherMatcher(Either, Matcher> matcher) { + private EitherMatcher(Either, Matcher> matcher) { this.matcher = matcher; } @@ -44,11 +45,19 @@ public void describeTo(Description description) { .unsafePerformIO(); } - public static EitherMatcher isLeftThat(Matcher lMatcher) { + public static EitherMatcher isLeftThat(Matcher lMatcher) { return new EitherMatcher<>(left(lMatcher)); } - public static EitherMatcher isRightThat(Matcher rMatcher) { + public static EitherMatcher isLeft() { + return isLeftThat(anything()); + } + + public static EitherMatcher isRightThat(Matcher rMatcher) { return new EitherMatcher<>(right(rMatcher)); } + + public static EitherMatcher isRight() { + return isRightThat(anything()); + } } From 777949bc0f332d1966d76507f8e4a09d9072a7a3 Mon Sep 17 00:00:00 2001 From: jnape Date: Wed, 23 Dec 2020 18:29:46 -0600 Subject: [PATCH 31/49] Adding IterateT#runStep for supporting interleaving semantics --- CHANGELOG.md | 5 ++- .../monad/transformer/builtin/IterateT.java | 39 ++++++++++++++----- .../transformer/builtin/IterateTTest.java | 36 +++++++++++++++++ 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c95ef614..13f3971a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] -There are currently no unreleased changes. +### Added + +- `IterateT#runStep`, a method used to run a single step of an IterateT without the contractual + guarantee of emitting a value or reaching the end ## [5.3.0] - 2020-12-07 diff --git a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java index d651795b5..cb719060b 100644 --- a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java +++ b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateT.java @@ -65,8 +65,7 @@ * @param the effect type * @param the element type */ -public class IterateT, A> implements - MonadT, IterateT> { +public class IterateT, A> implements MonadT, IterateT> { private final Pure pureM; private final ImmutableQueue>>, M>>, MonadRec>> spine; @@ -84,15 +83,35 @@ private IterateT(Pure pureM, * @return the embedded {@link Monad} */ public >>, M>> MMTA runIterateT() { - MonadRec>>, M>>, MonadRec>>, M> - mSpine = pureM.apply(spine); - return mSpine.trampolineM(tSpine -> tSpine.head().>>, M>>, MonadRec>>, Maybe>>>, M>>match( - ___ -> pureM.apply(terminate(nothing())), + return pureM., MonadRec, M>>apply(this) + .>>>trampolineM(iterateT -> iterateT.runStep() + .fmap(maybeMore -> maybeMore.match( + fn0(() -> terminate(nothing())), + t -> t.into((Maybe maybeA, IterateT as) -> maybeA.match( + fn0(() -> recurse(as)), + a -> terminate(just(tuple(a, as)))))))) + .coerce(); + } + + /** + * Run a single step of this {@link IterateT}, where a step is the smallest amount of work that could possibly be + * productive in advancing through the {@link IterateT}. Useful for implementing interleaving algorithms that + * require {@link IterateT IterateTs} to yield, emit, or terminate as soon as possible, regardless of whether the + * next element is readily available. + * + * @param the witnessed target type of the step + * @return the step + */ + public , IterateT>>, M>> MStep runStep() { + return spine.head().match( + fn0(() -> pureM., IterateT>>, MStep>apply(nothing())), thunkOrReal -> thunkOrReal.match( - thunk -> thunk.apply().fmap(m -> m.match( - ___ -> recurse(tSpine.tail()), - t -> terminate(just(t.fmap(as -> new IterateT<>(pureM, as.spine.concat(tSpine.tail()))))))), - real -> real.fmap(a -> terminate(just(tuple(a, new IterateT<>(pureM, tSpine.tail())))))))).coerce(); + thunk -> thunk.apply()., IterateT>>>fmap(m -> m.match( + fn0(() -> just(tuple(nothing(), new IterateT<>(pureM, spine.tail())))), + t -> just(t.biMap(Maybe::just, + as -> new IterateT<>(pureM, as.spine.concat(spine.tail())))))) + .coerce(), + ma -> ma.fmap(a -> just(tuple(just(a), new IterateT<>(pureM, spine.tail())))).coerce())); } /** diff --git a/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java b/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java index a0e974d43..7c2997ca6 100644 --- a/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java +++ b/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/IterateTTest.java @@ -48,6 +48,7 @@ import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.of; import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.pureIterateT; import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.singleton; +import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.suspended; import static com.jnape.palatable.lambda.monad.transformer.builtin.IterateT.unfold; import static com.jnape.palatable.lambda.monoid.builtin.AddAll.addAll; import static com.jnape.palatable.lambda.monoid.builtin.Join.join; @@ -313,4 +314,39 @@ public void flatMapCostsNoMoreEffortThanRequiredToYieldFirstValue() { assertEquals(1, flatMapCost.get()); assertEquals(1, unfoldCost.get()); } + + @Test + public void runStep() { + assertEquals(new Identity<>(nothing()), + IterateT., Integer>empty(pureIdentity()) + ., IterateT, Integer>>>>>runStep()); + + Tuple2, IterateT, Integer>> singletonStep = + singleton(new Identity<>(1)) + ., IterateT, Integer>>>>>runStep() + .runIdentity() + .orElseThrow(AssertionError::new); + assertEquals(just(1), singletonStep._1()); + assertThat(singletonStep._2(), isEmpty()); + + Tuple2, IterateT, Integer>> emptySuspendedStep = + IterateT., Integer>suspended(() -> new Identity<>(nothing()), + pureIdentity()) + ., IterateT, Integer>>>>>runStep() + .runIdentity() + .orElseThrow(AssertionError::new); + + assertEquals(nothing(), emptySuspendedStep._1()); + assertThat(emptySuspendedStep._2(), isEmpty()); + + Tuple2, IterateT, Integer>> nonEmptySuspendedStep = + suspended(() -> new Identity<>(just(tuple(1, empty(pureIdentity())))), + pureIdentity()) + ., IterateT, Integer>>>>>runStep() + .runIdentity() + .orElseThrow(AssertionError::new); + + assertEquals(just(1), nonEmptySuspendedStep._1()); + assertThat(nonEmptySuspendedStep._2(), isEmpty()); + } } \ No newline at end of file From 7af1b9c478ede6c755045b75e7d342572d73d82f Mon Sep 17 00:00:00 2001 From: Armando Cordova <18663098+corlaez@users.noreply.github.com> Date: Mon, 15 Mar 2021 16:52:59 -0400 Subject: [PATCH 32/49] Absent folds short-circuit (#116) --- .../lambda/semigroup/builtin/Absent.java | 48 ++++++++++- .../lambda/semigroup/builtin/AbsentTest.java | 80 ++++++++++++++++++- 2 files changed, 120 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Absent.java b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Absent.java index a692eac77..68162ad74 100644 --- a/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Absent.java +++ b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Absent.java @@ -2,11 +2,21 @@ import com.jnape.palatable.lambda.adt.Maybe; import com.jnape.palatable.lambda.functions.Fn1; -import com.jnape.palatable.lambda.functions.builtin.fn3.LiftA2; +import com.jnape.palatable.lambda.functions.builtin.fn3.FoldRight; import com.jnape.palatable.lambda.functions.specialized.SemigroupFactory; +import com.jnape.palatable.lambda.functor.builtin.Lazy; import com.jnape.palatable.lambda.monoid.builtin.Present; import com.jnape.palatable.lambda.semigroup.Semigroup; +import static com.jnape.palatable.lambda.adt.Maybe.nothing; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Into.into; +import static com.jnape.palatable.lambda.functions.builtin.fn3.LiftA2.liftA2; +import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.recurse; +import static com.jnape.palatable.lambda.functions.recursion.RecursiveResult.terminate; +import static com.jnape.palatable.lambda.functions.recursion.Trampoline.trampoline; +import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy; + /** * A {@link Semigroup} instance formed by {@link Maybe}<A> and a semigroup over A. The * application to two {@link Maybe} values is absence-biased, such that for a given {@link Maybe} x @@ -32,7 +42,7 @@ private Absent() { @Override public Semigroup> checkedApply(Semigroup aSemigroup) { - return LiftA2., Maybe>liftA2(aSemigroup)::apply; + return shortCircuitSemigroup(aSemigroup); } @SuppressWarnings("unchecked") @@ -40,8 +50,8 @@ public static Absent absent() { return (Absent) INSTANCE; } - public static Semigroup> absent(Semigroup semigroup) { - return Absent.absent().apply(semigroup); + public static Semigroup> absent(Semigroup aSemigroup) { + return shortCircuitSemigroup(aSemigroup); } public static Fn1, Maybe> absent(Semigroup aSemigroup, Maybe x) { @@ -51,4 +61,34 @@ public static Fn1, Maybe> absent(Semigroup aSemigroup, Maybe< public static Maybe absent(Semigroup semigroup, Maybe x, Maybe y) { return absent(semigroup, x).apply(y); } + + private static Semigroup> shortCircuitSemigroup(Semigroup aSemigroup) { + return new Semigroup>() { + @Override + public Maybe checkedApply(Maybe maybeX, Maybe maybeY) { + return liftA2(aSemigroup, maybeX, maybeY); + } + + @Override + public Maybe foldLeft(Maybe acc, Iterable> maybes) { + return trampoline( + into((res, it) -> res.equals(nothing()) + ? terminate(res) + : recurse(tuple(liftA2(aSemigroup, res, it.next()), it))), + tuple(acc, maybes.iterator())); + } + + @Override + public Lazy> foldRight(Maybe accumulation, Iterable> as) { + boolean shouldShortCircuit = accumulation == nothing(); + if (shouldShortCircuit) + return lazy(accumulation); + return FoldRight.foldRight( + (maybeX, acc) -> maybeX.lazyZip(acc.fmap(maybeY -> maybeY.fmap(aSemigroup.flip()))), + lazy(accumulation), + as + ); + } + }; + } } diff --git a/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/AbsentTest.java b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/AbsentTest.java index 5ef25395e..5ab7b53f6 100644 --- a/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/AbsentTest.java +++ b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/AbsentTest.java @@ -1,10 +1,17 @@ package com.jnape.palatable.lambda.semigroup.builtin; +import com.jnape.palatable.lambda.adt.Maybe; +import com.jnape.palatable.lambda.adt.Unit; +import com.jnape.palatable.lambda.functions.builtin.fn1.Constantly; import com.jnape.palatable.lambda.semigroup.Semigroup; import org.junit.Test; +import java.util.Arrays; + import static com.jnape.palatable.lambda.adt.Maybe.just; import static com.jnape.palatable.lambda.adt.Maybe.nothing; +import static com.jnape.palatable.lambda.adt.Unit.UNIT; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat; import static com.jnape.palatable.lambda.semigroup.builtin.Absent.absent; import static org.junit.Assert.assertEquals; @@ -12,12 +19,77 @@ public class AbsentTest { @Test public void semigroup() { + Semigroup addition = Integer::sum; + + assertEquals(just(3), absent(addition, just(1), just(2))); + assertEquals(nothing(), absent(addition, nothing(), just(1))); + assertEquals(nothing(), absent(addition, just(1), nothing())); + assertEquals(nothing(), absent(addition, nothing(), nothing())); + } + + @Test + public void foldRight() { + Absent absent = absent(); + Semigroup addition = Integer::sum; + + assertEquals(just(3), absent.apply(addition).foldRight(just(0), Arrays.asList(just(1), just(2))).value()); + assertEquals(nothing(), absent.apply(addition).foldRight(just(0), Arrays.asList(nothing(), just(1))).value()); + assertEquals(nothing(), absent.apply(addition).foldRight(just(0), Arrays.asList(just(1), nothing())).value()); + assertEquals(nothing(), absent.apply(addition).foldRight(just(0), Arrays.asList(nothing(), nothing())).value()); + } + + @Test + public void foldLeft() { Absent absent = absent(); Semigroup addition = Integer::sum; - assertEquals(just(3), absent.apply(addition, just(1), just(2))); - assertEquals(nothing(), absent.apply(addition, nothing(), just(1))); - assertEquals(nothing(), absent.apply(addition, just(1), nothing())); - assertEquals(nothing(), absent.apply(addition, nothing(), nothing())); + assertEquals(just(3), absent.apply(addition).foldLeft(just(0), Arrays.asList(just(1), just(2)))); + assertEquals(nothing(), absent.apply(addition).foldLeft(just(0), Arrays.asList(nothing(), just(1)))); + assertEquals(nothing(), absent.apply(addition).foldLeft(just(0), Arrays.asList(just(1), nothing()))); + assertEquals(nothing(), absent.apply(addition).foldLeft(just(0), Arrays.asList(nothing(), nothing()))); + } + + @Test(timeout = 200) + public void foldRightShortCircuit() { + Maybe result = Absent.absent(Constantly::constantly) + .foldRight(just(UNIT), repeat(nothing())).value(); + assertEquals(nothing(), result); + + result = Absent.absent(Constantly::constantly) + .foldRight(nothing(), repeat(just(UNIT))).value(); + assertEquals(nothing(), result); + } + + @Test(timeout = 200) + public void foldLeftShortCircuit() { + Maybe result = Absent.absent(Constantly::constantly) + .foldLeft(just(UNIT), repeat(nothing())); + assertEquals(nothing(), result); + + result = Absent.absent(Constantly::constantly) + .foldLeft(nothing(), repeat(just(UNIT))); + assertEquals(nothing(), result); + } + + @Test(timeout = 200) + public void checkedApplyFoldRightShortCircuit() { + Maybe result = Absent.absent().checkedApply(Constantly::constantly) + .foldRight(just(UNIT), repeat(nothing())).value(); + assertEquals(nothing(), result); + + result = Absent.absent().checkedApply(Constantly::constantly) + .foldRight(nothing(), repeat(just(UNIT))).value(); + assertEquals(nothing(), result); + } + + @Test(timeout = 200) + public void checkedApplyFoldLeftShortCircuit() { + Maybe result = Absent.absent().checkedApply(Constantly::constantly) + .foldLeft(just(UNIT), repeat(nothing())); + assertEquals(nothing(), result); + + result = Absent.absent().checkedApply(Constantly::constantly) + .foldLeft(nothing(), repeat(just(UNIT))); + assertEquals(nothing(), result); } } \ No newline at end of file From 31ad72636a68c37b2fb6988afb276dc20d8c1ab1 Mon Sep 17 00:00:00 2001 From: Alexander Bandukwala <7h3kk1d@gmail.com> Date: Sun, 9 May 2021 13:39:12 -0500 Subject: [PATCH 33/49] Add These::fromMaybes (#117) --- .../com/jnape/palatable/lambda/adt/These.java | 19 +++++++++++++++++++ .../jnape/palatable/lambda/adt/TheseTest.java | 11 +++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/main/java/com/jnape/palatable/lambda/adt/These.java b/src/main/java/com/jnape/palatable/lambda/adt/These.java index 6721d33e8..4fc4cc27b 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/These.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/These.java @@ -189,6 +189,25 @@ public static These both(A a, B b) { return new Both<>(tuple(a, b)); } + /** + * Convenience method for converting a pair of {@link Maybe}s into a {@link Maybe} of {@link These}. If both + * {@link Maybe}s are {@link Maybe#just} then the result is a {@link Maybe#just} {@link These#both}. If only one + * {@link Maybe} is {@link Maybe#just} then it will be {@link Maybe#just} {@link These#a} or + * {@link Maybe#just} {@link These#b}. If both {@link Maybe}s are {@link Maybe#nothing} then the result will be + * {@link Maybe#nothing}. + * + * @param maybeA the first optional value + * @param maybeB the second optional value + * @param the first possible type + * @param the second possible type + * @return the wrapped values as a {@link Maybe}<{@link These}<A,B>> + */ + public static Maybe> fromMaybes(Maybe maybeA, Maybe maybeB) { + return maybeA.fmap(a -> maybeB.fmap(b -> both(a, b)).orElse(a(a))) + .fmap(Maybe::just) + .orElse(maybeB.fmap(These::b)); + } + /** * The canonical {@link Pure} instance for {@link These}. * diff --git a/src/test/java/com/jnape/palatable/lambda/adt/TheseTest.java b/src/test/java/com/jnape/palatable/lambda/adt/TheseTest.java index 36a195011..e9f764d49 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/TheseTest.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/TheseTest.java @@ -12,9 +12,12 @@ import testsupport.traits.MonadRecLaws; import testsupport.traits.TraversableLaws; +import static com.jnape.palatable.lambda.adt.Maybe.just; +import static com.jnape.palatable.lambda.adt.Maybe.nothing; import static com.jnape.palatable.lambda.adt.These.a; import static com.jnape.palatable.lambda.adt.These.b; import static com.jnape.palatable.lambda.adt.These.both; +import static com.jnape.palatable.lambda.adt.These.fromMaybes; import static com.jnape.palatable.lambda.functor.builtin.Lazy.lazy; import static com.jnape.palatable.traitor.framework.Subjects.subjects; import static org.junit.Assert.assertEquals; @@ -48,4 +51,12 @@ public void staticPure() { These these = These.pureThese().apply(1); assertEquals(b(1), these); } + + @Test + public void fromMaybesPermutations() { + assertEquals(nothing(), fromMaybes(nothing(), nothing())); + assertEquals(just(These.a(1)), fromMaybes(just(1), nothing())); + assertEquals(just(These.b(1)), fromMaybes(nothing(), just(1))); + assertEquals(just(These.both(1, "hello")), fromMaybes(just(1), just("hello"))); + } } \ No newline at end of file From eca9d40ef7a295c57c16b75086af04f9d7989282 Mon Sep 17 00:00:00 2001 From: jnape Date: Tue, 31 Aug 2021 12:12:01 -0500 Subject: [PATCH 34/49] - WriterT#pure now has direct access to embedded monad's pure - EitherMatcher#isLeftOf/isRightOf for equality matching - updating CHANGELOG --- CHANGELOG.md | 13 ++++++++++-- .../monad/transformer/builtin/WriterT.java | 21 +++++++++++-------- .../testsupport/matchers/EitherMatcher.java | 9 ++++++++ 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13f3971a8..314f61106 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,23 @@ # Change Log + All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] +### Changed +- `Absent` folds short-circuit on the first `nothing()` +- `EitherMatcher#isLeftThat/isRightThat` support contravariant bounds on their delegates + ### Added +- `IterateT#runStep`, a method used to run a single step of an IterateT without the contractual guarantee of emitting a + value or reaching the end +- `These#fromMaybes :: Maybe a -> Maybe b -> Maybe (These a b)` +- `EitherMatcher#isLeftOf/isRightOf` for asserting equality -- `IterateT#runStep`, a method used to run a single step of an IterateT without the contractual - guarantee of emitting a value or reaching the end +### Fixed +- `WriterT` now keeps an immediate reference to the embedded monad's `pure` ## [5.3.0] - 2020-12-07 diff --git a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/WriterT.java b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/WriterT.java index 7a0205543..c8625bbf8 100644 --- a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/WriterT.java +++ b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/WriterT.java @@ -34,9 +34,12 @@ public final class WriterT, A> implements MonadWriter>, MonadT, WriterT> { + private final Pure pureM; private final Fn1, ? extends MonadRec, M>> writerFn; - private WriterT(Fn1, ? extends MonadRec, M>> writerFn) { + private WriterT(Pure pureM, + Fn1, ? extends MonadRec, M>> writerFn) { + this.pureM = pureM; this.writerFn = writerFn; } @@ -82,7 +85,7 @@ public > MW execWriterT(Monoid monoid) { */ @Override public WriterT> listens(Fn1 fn) { - return new WriterT<>(writerFn.fmap(m -> m.fmap(into((a, w) -> both(both(constantly(a), fn), id(), w))))); + return new WriterT<>(pureM, writerFn.fmap(m -> m.fmap(into((a, w) -> both(both(constantly(a), fn), id(), w))))); } /** @@ -90,7 +93,7 @@ public WriterT> listens(Fn1 fn) { */ @Override public WriterT censor(Fn1 fn) { - return new WriterT<>(writerFn.fmap(mt -> mt.fmap(t -> t.fmap(fn)))); + return new WriterT<>(pureM, writerFn.fmap(mt -> mt.fmap(t -> t.fmap(fn)))); } /** @@ -107,7 +110,7 @@ public > WriterT lift(MonadRec mb) { @Override public WriterT trampolineM( Fn1, WriterT>> fn) { - return new WriterT<>(monoid -> runWriterT(monoid).trampolineM(into((a, w) -> fn.apply(a) + return new WriterT<>(pureM, monoid -> runWriterT(monoid).trampolineM(into((a, w) -> fn.apply(a) .>>coerce() .runWriterT(monoid).fmap(t -> t.fmap(monoid.apply(w))) .fmap(into((aOrB, w_) -> aOrB.biMap(a_ -> tuple(a_, w_), b -> tuple(b, w_))))))); @@ -126,7 +129,7 @@ public WriterT fmap(Fn1 fn) { */ @Override public WriterT pure(B b) { - return new WriterT<>(m -> runWriterT(m).pure(tuple(b, m.identity()))); + return new WriterT<>(pureM, m -> pureM.apply(tuple(b, m.identity()))); } /** @@ -134,7 +137,7 @@ public WriterT pure(B b) { */ @Override public WriterT flatMap(Fn1>> f) { - return new WriterT<>(monoid -> writerFn.apply(monoid) + return new WriterT<>(pureM, monoid -> writerFn.apply(monoid) .flatMap(into((a, w) -> f.apply(a).>coerce().runWriterT(monoid) .fmap(t -> t.fmap(monoid.apply(w)))))); } @@ -144,7 +147,7 @@ public WriterT flatMap(Fn1 WriterT zip(Applicative, WriterT> appFn) { - return new WriterT<>(monoid -> runWriterT(monoid) + return new WriterT<>(pureM, monoid -> runWriterT(monoid) .zip(appFn.>>coerce().runWriterT(monoid) .fmap(into((f, y) -> into((a, x) -> tuple(f.apply(a), monoid.apply(x, y))))))); } @@ -196,7 +199,7 @@ public static > WriterT tell(MonadRec, A> WriterT listen(MonadRec ma) { - return new WriterT<>(monoid -> ma.fmap(a -> tuple(a, monoid.identity()))); + return new WriterT<>(Pure.of(ma), monoid -> ma.fmap(a -> tuple(a, monoid.identity()))); } /** @@ -209,7 +212,7 @@ public static , A> WriterT listen(MonadRec< * @return the {@link WriterT} */ public static , A> WriterT writerT(MonadRec, M> maw) { - return new WriterT<>(constantly(maw)); + return new WriterT<>(Pure.of(maw), constantly(maw)); } /** diff --git a/src/test/java/testsupport/matchers/EitherMatcher.java b/src/test/java/testsupport/matchers/EitherMatcher.java index 2023a22b2..b18f68af6 100644 --- a/src/test/java/testsupport/matchers/EitherMatcher.java +++ b/src/test/java/testsupport/matchers/EitherMatcher.java @@ -10,6 +10,7 @@ import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly; import static com.jnape.palatable.lambda.io.IO.io; import static org.hamcrest.CoreMatchers.anything; +import static org.hamcrest.CoreMatchers.equalTo; public final class EitherMatcher extends TypeSafeMatcher> { private final Either, Matcher> matcher; @@ -53,6 +54,10 @@ public static EitherMatcher isLeft() { return isLeftThat(anything()); } + public static EitherMatcher isLeftOf(L l) { + return isLeftThat(equalTo(l)); + } + public static EitherMatcher isRightThat(Matcher rMatcher) { return new EitherMatcher<>(right(rMatcher)); } @@ -60,4 +65,8 @@ public static EitherMatcher isRightThat(Matcher rMatcher public static EitherMatcher isRight() { return isRightThat(anything()); } + + public static EitherMatcher isRightOf(R r) { + return isRightThat(equalTo(r)); + } } From c3b262b38cc82ed4148662e1e343a67e81519a68 Mon Sep 17 00:00:00 2001 From: jnape Date: Tue, 31 Aug 2021 12:20:24 -0500 Subject: [PATCH 35/49] [maven-release-plugin] prepare release lambda-5.4.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cc4cfc72d..231ca6768 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ lambda - 5.3.1-SNAPSHOT + 5.4.0 jar Lambda From 0d2d287349acc657918387898c56ff1e071b37d5 Mon Sep 17 00:00:00 2001 From: jnape Date: Tue, 31 Aug 2021 12:20:26 -0500 Subject: [PATCH 36/49] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 231ca6768..6f7735144 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ lambda - 5.4.0 + 5.4.1-SNAPSHOT jar Lambda From 6ce542df28a94ad69e72b226564610b4d8919cfa Mon Sep 17 00:00:00 2001 From: jnape Date: Sat, 18 Sep 2021 13:19:54 -0500 Subject: [PATCH 37/49] Updating README and CHANGELOG --- CHANGELOG.md | 7 ++++++- README.md | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 314f61106..2d56ef532 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] +There are currently no unreleased changes + +## [5.4.0] - 2021-09-17 + ### Changed - `Absent` folds short-circuit on the first `nothing()` - `EitherMatcher#isLeftThat/isRightThat` support contravariant bounds on their delegates @@ -592,7 +596,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `Monadic/Dyadic/TriadicFunction`, `Predicate`, `Tuple2`, `Tuple3` - `Functor`, `BiFunctor`, `ProFunctor` -[Unreleased]: https://github.com/palatable/lambda/compare/lambda-5.3.0...HEAD +[Unreleased]: https://github.com/palatable/lambda/compare/lambda-5.4.0...HEAD +[5.4.0]: https://github.com/palatable/lambda/compare/lambda-5.3.0...lambda-5.4.0 [5.3.0]: https://github.com/palatable/lambda/compare/lambda-5.2.0...lambda-5.3.0 [5.2.0]: https://github.com/palatable/lambda/compare/lambda-5.1.0...lambda-5.2.0 [5.1.0]: https://github.com/palatable/lambda/compare/lambda-5.0.0...lambda-5.1.0 diff --git a/README.md b/README.md index 060d4e9ee..850dcfb8f 100644 --- a/README.md +++ b/README.md @@ -61,14 +61,14 @@ Add the following dependency to your: com.jnape.palatable lambda - 5.3.0 + 5.4.0 ``` `build.gradle` ([Gradle](https://docs.gradle.org/current/userguide/dependency_management.html)): ```gradle -compile group: 'com.jnape.palatable', name: 'lambda', version: '5.3.0' +compile group: 'com.jnape.palatable', name: 'lambda', version: '5.4.0' ``` Examples From 5e73e1f77c826c6342792186afdab351a6c40def Mon Sep 17 00:00:00 2001 From: Alexander Bandukwala <7h3kk1d@gmail.com> Date: Thu, 10 Mar 2022 18:00:38 -0600 Subject: [PATCH 38/49] Add ReaderT#ask (#119) --- .../lambda/monad/transformer/builtin/ReaderT.java | 13 +++++++++++++ .../monad/transformer/builtin/ReaderTTest.java | 7 +++++++ 2 files changed, 20 insertions(+) diff --git a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/ReaderT.java b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/ReaderT.java index e7f93a168..e4c8ec5f0 100644 --- a/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/ReaderT.java +++ b/src/main/java/com/jnape/palatable/lambda/monad/transformer/builtin/ReaderT.java @@ -204,6 +204,19 @@ public ReaderT> carry() { return (ReaderT>) Cartesian.super.carry(); } + /** + * Given a {@link Pure} ask will give you access to the input within the monadic embedding + * + * @param pureM the {@link Pure} instance for the given {@link Monad} + * @param the input and output type of the returned ReaderT + * @param the returned {@link Monad} + * @return the {@link ReaderT} + */ + public static > ReaderT ask(Pure pureM) { + //noinspection Convert2MethodRef + return readerT(a -> pureM.apply(a)); + } + /** * Lift a {@link Fn1 function} (R -> {@link Monad}<A, M>) into a {@link ReaderT} instance. * diff --git a/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/ReaderTTest.java b/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/ReaderTTest.java index cbcf1ef8b..983e7113a 100644 --- a/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/ReaderTTest.java +++ b/src/test/java/com/jnape/palatable/lambda/monad/transformer/builtin/ReaderTTest.java @@ -97,4 +97,11 @@ public void fmapInteractions() { readerT.fmap(plusOne).fmap(plusOne).fmap(plusOne).runReaderT(0); assertEquals(1, invocations.get()); } + + @Test + public void askRetrievesInput() { + assertEquals(new Identity<>(1), + ReaderT.>ask(pureIdentity()) + .>runReaderT(1)); + } } \ No newline at end of file From 418cfa108db54f27aaaa4a2e56ee87ec95a62f56 Mon Sep 17 00:00:00 2001 From: Alexander Bandukwala <7h3kk1d@gmail.com> Date: Sun, 13 Mar 2022 19:09:40 -0500 Subject: [PATCH 39/49] Fix DropWhile fusion bug (#120) --- .../iteration/PredicatedDroppingIterable.java | 17 +++++-------- .../iteration/PredicatedDroppingIterator.java | 25 +++++++++++-------- .../iteration/RewindableIterator.java | 4 +-- .../functions/builtin/fn2/DropWhileTest.java | 10 ++++++-- .../PredicatedDroppingIteratorTest.java | 5 +++- .../iteration/RewindableIteratorTest.java | 6 +++-- 6 files changed, 38 insertions(+), 29 deletions(-) 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 d87cf0e09..ea7b68bc7 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 @@ -1,24 +1,20 @@ package com.jnape.palatable.lambda.internal.iteration; import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.internal.ImmutableQueue; -import java.util.ArrayList; import java.util.Iterator; -import java.util.List; - -import static com.jnape.palatable.lambda.functions.builtin.fn2.Any.any; -import static java.util.Collections.singletonList; public final class PredicatedDroppingIterable implements Iterable { - private final List> predicates; - private final Iterable as; + private final ImmutableQueue> predicates; + private final Iterable as; public PredicatedDroppingIterable(Fn1 predicate, Iterable as) { - List> predicates = new ArrayList<>(singletonList(predicate)); + ImmutableQueue> predicates = ImmutableQueue.singleton(predicate); while (as instanceof PredicatedDroppingIterable) { PredicatedDroppingIterable nested = (PredicatedDroppingIterable) as; as = nested.as; - predicates.addAll(0, nested.predicates); + predicates = nested.predicates.concat(predicates); } this.predicates = predicates; this.as = as; @@ -26,7 +22,6 @@ public PredicatedDroppingIterable(Fn1 predicate, I @Override public Iterator iterator() { - Fn1 metaPredicate = a -> any(p -> p.apply(a), predicates); - return new PredicatedDroppingIterator<>(metaPredicate, as.iterator()); + return new PredicatedDroppingIterator<>(predicates, as.iterator()); } } diff --git a/src/main/java/com/jnape/palatable/lambda/internal/iteration/PredicatedDroppingIterator.java b/src/main/java/com/jnape/palatable/lambda/internal/iteration/PredicatedDroppingIterator.java index 7add62a45..a97b513ac 100644 --- a/src/main/java/com/jnape/palatable/lambda/internal/iteration/PredicatedDroppingIterator.java +++ b/src/main/java/com/jnape/palatable/lambda/internal/iteration/PredicatedDroppingIterator.java @@ -1,19 +1,18 @@ package com.jnape.palatable.lambda.internal.iteration; import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.internal.ImmutableQueue; import java.util.Iterator; import java.util.NoSuchElementException; public final class PredicatedDroppingIterator extends ImmutableIterator { - private final Fn1 predicate; - private final RewindableIterator rewindableIterator; - private boolean finishedDropping; + private final Iterator> predicates; + private final RewindableIterator rewindableIterator; - public PredicatedDroppingIterator(Fn1 predicate, Iterator asIterator) { - this.predicate = predicate; + public PredicatedDroppingIterator(ImmutableQueue> predicates, Iterator asIterator) { + this.predicates = predicates.iterator(); rewindableIterator = new RewindableIterator<>(asIterator); - finishedDropping = false; } @Override @@ -31,11 +30,17 @@ public A next() { } private void dropElementsIfNecessary() { - while (rewindableIterator.hasNext() && !finishedDropping) { - if (!predicate.apply(rewindableIterator.next())) { - rewindableIterator.rewind(); - finishedDropping = true; + while (predicates.hasNext() && rewindableIterator.hasNext()) { + Fn1 predicate = predicates.next(); + boolean predicateDone = false; + + while (rewindableIterator.hasNext() && !predicateDone) { + if (!predicate.apply(rewindableIterator.next())) { + rewindableIterator.rewind(); + predicateDone = true; + } } + } } } diff --git a/src/main/java/com/jnape/palatable/lambda/internal/iteration/RewindableIterator.java b/src/main/java/com/jnape/palatable/lambda/internal/iteration/RewindableIterator.java index 5bdde4312..092e7e98b 100644 --- a/src/main/java/com/jnape/palatable/lambda/internal/iteration/RewindableIterator.java +++ b/src/main/java/com/jnape/palatable/lambda/internal/iteration/RewindableIterator.java @@ -54,9 +54,7 @@ public A retrieve() { if (cache == null) throw new NoSuchElementException("Cache is empty."); - A cache = this.cache; - this.cache = null; - return cache; + return this.cache; } public boolean isEmpty() { diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/DropWhileTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/DropWhileTest.java index 83903f451..ff4551a11 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/DropWhileTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/DropWhileTest.java @@ -56,7 +56,13 @@ public void deforestingExecutesPredicatesInOrder() { innerInvocations.add(x); return x > 2; }, asList(1, 2, 3))).forEach(__ -> {}); - assertThat(innerInvocations, iterates(1, 2, 3)); - assertThat(outerInvocations, iterates(1, 2)); + assertThat(innerInvocations, iterates(1)); + assertThat(outerInvocations, iterates(1, 2, 3)); + } + + @Test + public void eachLayerIsAppliedOnce() { + assertThat(dropWhile(i -> i % 2 == 0, dropWhile(i -> i % 2 == 1, asList(1, 2, 3))), + iterates(3)); } } diff --git a/src/test/java/com/jnape/palatable/lambda/internal/iteration/PredicatedDroppingIteratorTest.java b/src/test/java/com/jnape/palatable/lambda/internal/iteration/PredicatedDroppingIteratorTest.java index 7fb0613c3..44fcde9f1 100644 --- a/src/test/java/com/jnape/palatable/lambda/internal/iteration/PredicatedDroppingIteratorTest.java +++ b/src/test/java/com/jnape/palatable/lambda/internal/iteration/PredicatedDroppingIteratorTest.java @@ -1,15 +1,18 @@ package com.jnape.palatable.lambda.internal.iteration; import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.internal.ImmutableQueue; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import java.util.Collections; import java.util.Iterator; import java.util.NoSuchElementException; +import static java.util.Collections.singletonList; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static testsupport.Mocking.mockIteratorToHaveValues; @@ -25,7 +28,7 @@ public class PredicatedDroppingIteratorTest { @Before public void setUp() { - predicatedDroppingIterator = new PredicatedDroppingIterator<>(EVEN, iterator); + predicatedDroppingIterator = new PredicatedDroppingIterator<>(ImmutableQueue.singleton(EVEN), iterator); } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/internal/iteration/RewindableIteratorTest.java b/src/test/java/com/jnape/palatable/lambda/internal/iteration/RewindableIteratorTest.java index 33bda1ad4..f07fe55a5 100644 --- a/src/test/java/com/jnape/palatable/lambda/internal/iteration/RewindableIteratorTest.java +++ b/src/test/java/com/jnape/palatable/lambda/internal/iteration/RewindableIteratorTest.java @@ -10,6 +10,7 @@ import java.util.NoSuchElementException; import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static testsupport.Mocking.mockIteratorToHaveValues; @@ -51,13 +52,14 @@ public void cannotRewindIfNoValuesIterated() { rewindableIterator.rewind(); } - @Test(expected = NoSuchElementException.class) - public void cannotRewindTheSameElementTwice() { + @Test + public void canRewindTheSameElementTwice() { mockIteratorToHaveValues(iterator, 1, 2, 3); rewindableIterator.next(); rewindableIterator.rewind(); rewindableIterator.next(); rewindableIterator.rewind(); + assertEquals(1, rewindableIterator.next()); } @Test From 8a85105879ca654b646a0c3398c6ce803062af33 Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 13 Mar 2022 19:18:53 -0500 Subject: [PATCH 40/49] Updating CHANGELOG --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d56ef532..3aecf9b88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] -There are currently no unreleased changes +### Added +- `ReaderT#ask`, a static factory method for returning an identity `ReaderT` + +### Fixed +- nested `DropWhile`s no longer incorrectly deforest using disjunction ## [5.4.0] - 2021-09-17 From 0423ec9cf29423ea992eb01cbf36a5a1d950872e Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 13 Mar 2022 19:21:30 -0500 Subject: [PATCH 41/49] [maven-release-plugin] prepare release lambda-5.5.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6f7735144..d713a9681 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ lambda - 5.4.1-SNAPSHOT + 5.5.0 jar Lambda From abf79ddfe80cb8317dd2ff6bbd7cdca298984d55 Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 13 Mar 2022 19:21:32 -0500 Subject: [PATCH 42/49] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d713a9681..a44d3b634 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ lambda - 5.5.0 + 5.5.1-SNAPSHOT jar Lambda From 6e92250ee4f8b32388cb82d13c94a4613d42d123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Ho=C3=9F?= Date: Sat, 12 Nov 2022 15:05:43 +0100 Subject: [PATCH 43/49] add automatic module name to jar manifest This allows downstream projects using lambda to publish their own jars as modules without switching lambda itself to a jigsaw module. --- pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pom.xml b/pom.xml index a44d3b634..bae03b1e3 100644 --- a/pom.xml +++ b/pom.xml @@ -103,6 +103,13 @@ org.apache.maven.plugins maven-jar-plugin ${maven-jar-plugin.version} + + + + com.jnape.palatable.lambda + + + From 778fdfc42b3636c07aad70aac59440a9134fc64a Mon Sep 17 00:00:00 2001 From: Alexander Bandukwala <7h3kk1d@gmail.com> Date: Fri, 25 Nov 2022 20:21:28 -0600 Subject: [PATCH 44/49] GitHub action CI for prs (#124) * Enable CI for pull requests * Remove travis config --- .github/workflows/maven.yml | 4 +++- .travis.yml | 5 ----- 2 files changed, 3 insertions(+), 6 deletions(-) delete mode 100644 .travis.yml diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 873d5ac83..296a02977 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -1,6 +1,8 @@ name: Java CI -on: [push] +on: + push: + pull_request: jobs: build-java-1_8: diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 39074daea..000000000 --- a/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -dist: trusty -language: java -jdk: - - oraclejdk8 - - openjdk11 From edec5808c7863db98a4a9d41859044e0d2fb9e39 Mon Sep 17 00:00:00 2001 From: John Napier Date: Fri, 25 Nov 2022 20:22:15 -0600 Subject: [PATCH 45/49] Update README.md Removed travis build badge from README --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 850dcfb8f..a1d7400bc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ λ ====== -[![Build Status](https://travis-ci.com/palatable/lambda.svg?branch=master)](https://travis-ci.com/palatable/lambda) [![Actions Status](https://github.com/palatable/lambda/workflows/Java%20CI/badge.svg)](https://github.com/palatable/lambda/actions) [![Lambda](https://img.shields.io/maven-central/v/com.jnape.palatable/lambda.svg)](http://search.maven.org/#search%7Cga%7C1%7Ccom.jnape.palatable.lambda) [![Join the chat at https://gitter.im/palatable/lambda](https://badges.gitter.im/palatable/lambda.svg)](https://gitter.im/palatable/lambda?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) From 3fe363976c57d7613e4052c45d3e02b1571446f8 Mon Sep 17 00:00:00 2001 From: Alexander Bandukwala <7h3kk1d@gmail.com> Date: Fri, 2 Dec 2022 15:03:57 -0600 Subject: [PATCH 46/49] Fix Absent::foldLeft bug (#121) Co-authored-by: Alexander Bandukwala Co-authored-by: Skyler Lutz --- .../jnape/palatable/lambda/semigroup/builtin/Absent.java | 2 +- .../palatable/lambda/semigroup/builtin/AbsentTest.java | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Absent.java b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Absent.java index 68162ad74..222a5e7dc 100644 --- a/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Absent.java +++ b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Absent.java @@ -72,7 +72,7 @@ public Maybe checkedApply(Maybe maybeX, Maybe maybeY) { @Override public Maybe foldLeft(Maybe acc, Iterable> maybes) { return trampoline( - into((res, it) -> res.equals(nothing()) + into((res, it) -> res.equals(nothing()) || !it.hasNext() ? terminate(res) : recurse(tuple(liftA2(aSemigroup, res, it.next()), it))), tuple(acc, maybes.iterator())); diff --git a/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/AbsentTest.java b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/AbsentTest.java index 5ab7b53f6..9d3dc4524 100644 --- a/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/AbsentTest.java +++ b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/AbsentTest.java @@ -71,6 +71,13 @@ public void foldLeftShortCircuit() { assertEquals(nothing(), result); } + @Test + public void foldLeftWorksForJusts() { + Maybe result = Absent.absent(Constantly::constantly) + .foldLeft(just(UNIT), Arrays.asList(just(UNIT), just(UNIT))); + assertEquals(just(UNIT), result); + } + @Test(timeout = 200) public void checkedApplyFoldRightShortCircuit() { Maybe result = Absent.absent().checkedApply(Constantly::constantly) From 88e70febbe69067778242364d2398f897d725fe6 Mon Sep 17 00:00:00 2001 From: Alexander Bandukwala <7h3kk1d@gmail.com> Date: Fri, 31 Mar 2023 17:38:21 -0500 Subject: [PATCH 47/49] Switch chat badge to point to palatable discord --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a1d7400bc..11afcbe37 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ====== [![Actions Status](https://github.com/palatable/lambda/workflows/Java%20CI/badge.svg)](https://github.com/palatable/lambda/actions) [![Lambda](https://img.shields.io/maven-central/v/com.jnape.palatable/lambda.svg)](http://search.maven.org/#search%7Cga%7C1%7Ccom.jnape.palatable.lambda) -[![Join the chat at https://gitter.im/palatable/lambda](https://badges.gitter.im/palatable/lambda.svg)](https://gitter.im/palatable/lambda?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Join the chat on Discord](https://dcbadge.vercel.app/api/server/wR7k8RAKM5)](https://discord.gg/wR7k8RAKM5) [![Floobits Status](https://floobits.com/jnape/lambda.svg)](https://floobits.com/jnape/lambda/redirect) Functional patterns for Java From e4831180484a9b7eca8a7cbacff5dc1c3b0ed2ca Mon Sep 17 00:00:00 2001 From: Alexander Bandukwala <7h3kk1d@gmail.com> Date: Fri, 31 Mar 2023 17:49:49 -0500 Subject: [PATCH 48/49] Fix height of discord badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 11afcbe37..7d84c6295 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ====== [![Actions Status](https://github.com/palatable/lambda/workflows/Java%20CI/badge.svg)](https://github.com/palatable/lambda/actions) [![Lambda](https://img.shields.io/maven-central/v/com.jnape.palatable/lambda.svg)](http://search.maven.org/#search%7Cga%7C1%7Ccom.jnape.palatable.lambda) -[![Join the chat on Discord](https://dcbadge.vercel.app/api/server/wR7k8RAKM5)](https://discord.gg/wR7k8RAKM5) +[Join the chat on Discord](https://discord.gg/wR7k8RAKM5) [![Floobits Status](https://floobits.com/jnape/lambda.svg)](https://floobits.com/jnape/lambda/redirect) Functional patterns for Java From c1d2193ebdbbac8b0b31bec9bdcbfbd5820858ff Mon Sep 17 00:00:00 2001 From: Alexander Bandukwala <7h3kk1d@gmail.com> Date: Thu, 20 Apr 2023 21:36:44 -0500 Subject: [PATCH 49/49] FoldRight fixes for Monoid/Semigroup - Semigroup::foldRight had parameters flipped - Monoid::foldRight was accumulating the accumulator from the left. This has been fixed but the identity is now being used as a starting accumulator. Otherwise, the evaluation order is unchanged. - Changed ExplainFold parameter name since accumulator can be in either position --- .../java/com/jnape/palatable/lambda/monoid/Monoid.java | 2 +- .../com/jnape/palatable/lambda/semigroup/Semigroup.java | 2 +- .../lambda/functions/builtin/fn3/FoldRightTest.java | 2 +- .../com/jnape/palatable/lambda/monoid/MonoidTest.java | 9 +++++++++ .../jnape/palatable/lambda/semigroup/SemigroupTest.java | 9 +++++---- src/test/java/testsupport/functions/ExplainFold.java | 2 +- 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/Monoid.java b/src/main/java/com/jnape/palatable/lambda/monoid/Monoid.java index 8ecf83775..59f4ce1c7 100644 --- a/src/main/java/com/jnape/palatable/lambda/monoid/Monoid.java +++ b/src/main/java/com/jnape/palatable/lambda/monoid/Monoid.java @@ -82,7 +82,7 @@ default A foldLeft(A a, Iterable as) { */ @Override default Lazy foldRight(A a, Iterable as) { - return lazy(() -> flip().foldMap(id(), reverse(cons(a, as)))); + return lazy(() -> flip().foldMap(id(), cons(a, reverse(as)))); } /** diff --git a/src/main/java/com/jnape/palatable/lambda/semigroup/Semigroup.java b/src/main/java/com/jnape/palatable/lambda/semigroup/Semigroup.java index a061328ce..d4cc1e6b4 100644 --- a/src/main/java/com/jnape/palatable/lambda/semigroup/Semigroup.java +++ b/src/main/java/com/jnape/palatable/lambda/semigroup/Semigroup.java @@ -39,7 +39,7 @@ default A foldLeft(A a, Iterable as) { * @see FoldRight */ default Lazy foldRight(A a, Iterable as) { - return FoldRight.foldRight((y, lazyX) -> lazyX.fmap(x -> apply(x, y)), lazy(a), as); + return FoldRight.foldRight((y, lazyX) -> lazyX.fmap(x -> apply(y, x)), lazy(a), as); } /** diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn3/FoldRightTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn3/FoldRightTest.java index 912ee9b4a..62f6d43c9 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn3/FoldRightTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn3/FoldRightTest.java @@ -29,7 +29,7 @@ public Fn1, Iterable> createTestSubject() { @Test public void foldRightAccumulatesRightToLeft() { - assertThat(foldRight((a, lazyB) -> lazyB.fmap(b -> explainFold().apply(a, b)), + assertThat(foldRight((a, lazyAcc) -> lazyAcc.fmap(acc -> explainFold().apply(a, acc)), lazy("5"), asList("1", "2", "3", "4")) .value(), diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/MonoidTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/MonoidTest.java index 87cfced3e..135d87195 100644 --- a/src/test/java/com/jnape/palatable/lambda/monoid/MonoidTest.java +++ b/src/test/java/com/jnape/palatable/lambda/monoid/MonoidTest.java @@ -1,6 +1,7 @@ package com.jnape.palatable.lambda.monoid; import com.jnape.palatable.lambda.adt.Maybe; +import com.jnape.palatable.lambda.functor.builtin.Lazy; import org.junit.Test; import java.util.List; @@ -10,6 +11,7 @@ import static com.jnape.palatable.lambda.monoid.Monoid.monoid; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; +import static testsupport.functions.ExplainFold.explainFold; public class MonoidTest { @@ -25,6 +27,13 @@ public void reduceRight() { assertEquals((Integer) 6, sum.reduceRight(asList(1, 2, 3))); } + @Test + public void foldRight() { + Lazy lazyString = monoid(explainFold()::apply, "0") + .foldRight("4", asList("1", "2", "3")); + assertEquals("(1 + (2 + (3 + (4 + 0))))", lazyString.value()); + } + @Test public void foldMap() { Monoid sum = monoid(Integer::sum, 0); diff --git a/src/test/java/com/jnape/palatable/lambda/semigroup/SemigroupTest.java b/src/test/java/com/jnape/palatable/lambda/semigroup/SemigroupTest.java index 3291b6b1b..712c09e02 100644 --- a/src/test/java/com/jnape/palatable/lambda/semigroup/SemigroupTest.java +++ b/src/test/java/com/jnape/palatable/lambda/semigroup/SemigroupTest.java @@ -4,18 +4,19 @@ import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; +import static testsupport.functions.ExplainFold.explainFold; public class SemigroupTest { @Test public void foldLeft() { - Semigroup sum = (x, y) -> x + y; - assertEquals((Integer) 6, sum.foldLeft(0, asList(1, 2, 3))); + Semigroup foldFn = explainFold()::apply; + assertEquals("(((0 + 1) + 2) + 3)", foldFn.foldLeft("0", asList("1", "2", "3"))); } @Test public void foldRight() { - Semigroup sum = (x, y) -> x + y; - assertEquals((Integer) 6, sum.foldRight(0, asList(1, 2, 3)).value()); + Semigroup foldFn = explainFold()::apply; + assertEquals("(1 + (2 + (3 + 0)))", foldFn.foldRight("0", asList("1", "2", "3")).value()); } } \ No newline at end of file diff --git a/src/test/java/testsupport/functions/ExplainFold.java b/src/test/java/testsupport/functions/ExplainFold.java index d59a2378b..ba5c29527 100644 --- a/src/test/java/testsupport/functions/ExplainFold.java +++ b/src/test/java/testsupport/functions/ExplainFold.java @@ -7,6 +7,6 @@ public class ExplainFold { public static Fn2 explainFold() { - return (acc, x) -> format("(%s + %s)", acc, x); + return (x, y) -> format("(%s + %s)", x, y); } }