From f30d63fb5c5367aba49b2e319fef50788504553a Mon Sep 17 00:00:00 2001 From: jnape Date: Sat, 8 Feb 2020 17:58:13 -0600 Subject: [PATCH 01/25] [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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] #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/25] #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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] 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/25] [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