From a21fa6ec7329ea553060a748a91c593048497c07 Mon Sep 17 00:00:00 2001 From: jnape Date: Fri, 4 May 2018 02:32:10 -0500 Subject: [PATCH 01/14] [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 d990937e0..75fbb10f8 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ lambda - 3.0.0 + 3.0.1-SNAPSHOT jar Lambda From f4ca34b25c66ce1f089ad8622e777e2e2618c9c0 Mon Sep 17 00:00:00 2001 From: John Napier Date: Fri, 4 May 2018 02:36:03 -0500 Subject: [PATCH 02/14] Updating CHANGELOG to reflect release --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 405f7c8f0..3273cf69d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] +_No changes_ + +## [3.0.0] - 2018-05-04 ### Changed - ***Breaking Change***: `Sequence` now has two more type parameters to aid in inference - ***Breaking Change***: `Traversable#traverse` now has three more type parameters to aid in inference @@ -300,7 +303,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-2.1.1...HEAD +[Unreleased]: https://github.com/palatable/lambda/compare/lambda-3.0.0...HEAD +[3.0.0]: https://github.com/palatable/lambda/compare/lambda-2.1.1...lambda-3.0.0 [2.1.1]: https://github.com/palatable/lambda/compare/lambda-2.1.0...lambda-2.1.1 [2.1.0]: https://github.com/palatable/lambda/compare/lambda-2.0.0...lambda-2.1.0 [2.0.0]: https://github.com/palatable/lambda/compare/lambda-1.6.3...lambda-2.0.0 From 52c5928ade2cc760f63604f675192d0db1dbaf4a Mon Sep 17 00:00:00 2001 From: John Napier Date: Fri, 4 May 2018 02:37:47 -0500 Subject: [PATCH 03/14] Updating README for latest version --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dcfe6df73..5d8eff2e7 100644 --- a/README.md +++ b/README.md @@ -57,14 +57,14 @@ Add the following dependency to your: com.jnape.palatable lambda - 2.1.1 + 3.0.0 ``` `build.gradle` ([Gradle](https://docs.gradle.org/current/userguide/dependency_management.html)): ```gradle -compile group: 'com.jnape.palatable', name: 'lambda', version: '2.1.1' +compile group: 'com.jnape.palatable', name: 'lambda', version: '3.0.0' ``` Examples From 04d738d8d88335ad2da4c5fa77cf3ffbf2e7b4ff Mon Sep 17 00:00:00 2001 From: jnape Date: Mon, 7 May 2018 21:27:41 -0500 Subject: [PATCH 04/14] ToMap is covariant in Entry position --- CHANGELOG.md | 3 ++- .../palatable/lambda/functions/builtin/fn2/ToMap.java | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3273cf69d..8eba9013e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] -_No changes_ +### Changed +- `ToMap` accepts an `Iterable` covariant in `Map.Entry` ## [3.0.0] - 2018-05-04 ### Changed diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToMap.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToMap.java index fa69c61c5..1522720aa 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToMap.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToMap.java @@ -17,7 +17,7 @@ * @param the value element type * @param the resulting map type */ -public final class ToMap> implements Fn2, Iterable>, M> { +public final class ToMap> implements Fn2, Iterable>, M> { private static final ToMap INSTANCE = new ToMap<>(); @@ -25,7 +25,7 @@ private ToMap() { } @Override - public M apply(Supplier mSupplier, Iterable> entries) { + public M apply(Supplier mSupplier, Iterable> entries) { return foldLeft((m, kv) -> { m.put(kv.getKey(), kv.getValue()); return m; @@ -37,11 +37,12 @@ public static > ToMap toMap() { return INSTANCE; } - public static > Fn1>, M> toMap(Supplier mSupplier) { + public static > Fn1>, M> toMap(Supplier mSupplier) { return ToMap.toMap().apply(mSupplier); } - public static > M toMap(Supplier mSupplier, Iterable> entries) { + public static > M toMap(Supplier mSupplier, + Iterable> entries) { return toMap(mSupplier).apply(entries); } } From 06131c6cf8832c1e72c667b04ab62e3d55d7cf4b Mon Sep 17 00:00:00 2001 From: jnape Date: Mon, 7 May 2018 22:20:58 -0500 Subject: [PATCH 05/14] Adding better docs for MapLens#mappingValues --- .../java/com/jnape/palatable/lambda/lens/lenses/MapLens.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/jnape/palatable/lambda/lens/lenses/MapLens.java b/src/main/java/com/jnape/palatable/lambda/lens/lenses/MapLens.java index cb393ac1f..f412c1161 100644 --- a/src/main/java/com/jnape/palatable/lambda/lens/lenses/MapLens.java +++ b/src/main/java/com/jnape/palatable/lambda/lens/lenses/MapLens.java @@ -176,6 +176,9 @@ public static Lens.Simple, Map> mappingValues(Functi /** * A lens that focuses on a map while mapping its values with the mapping {@link Iso}. + *

+ * Note that for this lens to be lawful, iso must be bijective: that is, every V must + * uniquely and invertibly map to exactly one V2. * * @param iso the mapping {@link Iso} * @param the key type From 700d6253da8afea5556a6911fb07f65e77cb80ae Mon Sep 17 00:00:00 2001 From: jnape Date: Mon, 7 May 2018 23:52:20 -0500 Subject: [PATCH 06/14] Adding Upcast --- CHANGELOG.md | 3 ++ .../lambda/functions/builtin/fn1/Upcast.java | 43 +++++++++++++++++++ .../functions/builtin/fn1/UpcastTest.java | 18 ++++++++ 3 files changed, 64 insertions(+) create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Upcast.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/UpcastTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 8eba9013e..2d96d17d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ### Changed - `ToMap` accepts an `Iterable` covariant in `Map.Entry` +### Added +- `Upcast` for safely casting up a type hierarchy + ## [3.0.0] - 2018-05-04 ### Changed - ***Breaking Change***: `Sequence` now has two more type parameters to aid in inference diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Upcast.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Upcast.java new file mode 100644 index 000000000..08f6fe519 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Upcast.java @@ -0,0 +1,43 @@ +package com.jnape.palatable.lambda.functions.builtin.fn1; + +import com.jnape.palatable.lambda.functions.Fn1; + +/** + * Upcast a value of type B to a value of type A that B extends. This is + * principally useful when dealing with parametric types that are invariant in their parameters and a cast is + * necessary for compatibility purposes. + *

+ * Example: + *

+ * {@code
+ * Iterable have = new ArrayList<>();
+ * Iterable want = map(upcast(), have); // necessary due to invariance in parameter
+ * }
+ * 
+ *

+ * Note that this is universally safe. + * + * @param the covariant type + * @param the contravariant type + */ +public final class Upcast implements Fn1 { + + private static final Upcast INSTANCE = new Upcast<>(); + + private Upcast() { + } + + @Override + public B apply(A a) { + return a; + } + + @SuppressWarnings("unchecked") + public static Upcast upcast() { + return INSTANCE; + } + + public static B upcast(A a) { + return Upcast.upcast().apply(a); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/UpcastTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/UpcastTest.java new file mode 100644 index 000000000..a93d83537 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/UpcastTest.java @@ -0,0 +1,18 @@ +package com.jnape.palatable.lambda.functions.builtin.fn1; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.functions.builtin.fn1.Upcast.upcast; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Map.map; +import static java.util.Arrays.asList; + +public class UpcastTest { + + @Test + @SuppressWarnings("unused") + public void castsUp() { + Upcast upcast = upcast(); + Iterable strings = asList("foo", "bar"); + Iterable charSequences = map(upcast, strings); + } +} \ No newline at end of file From dfbc3faca9653bb242b098e43805497d418bd5f3 Mon Sep 17 00:00:00 2001 From: jnape Date: Fri, 11 May 2018 19:32:05 -0500 Subject: [PATCH 07/14] Adding SetLens, lenses that operate on Sets --- CHANGELOG.md | 1 + .../palatable/lambda/lens/lenses/SetLens.java | 51 +++++++++++++++++++ .../lambda/lens/lenses/SetLensTest.java | 41 +++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 src/main/java/com/jnape/palatable/lambda/lens/lenses/SetLens.java create mode 100644 src/test/java/com/jnape/palatable/lambda/lens/lenses/SetLensTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d96d17d0..e6a8e835b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ### Added - `Upcast` for safely casting up a type hierarchy +- `SetLens`, lenses operating on `Set`s ## [3.0.0] - 2018-05-04 ### Changed diff --git a/src/main/java/com/jnape/palatable/lambda/lens/lenses/SetLens.java b/src/main/java/com/jnape/palatable/lambda/lens/lenses/SetLens.java new file mode 100644 index 000000000..b48ceea24 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/lens/lenses/SetLens.java @@ -0,0 +1,51 @@ +package com.jnape.palatable.lambda.lens.lenses; + +import com.jnape.palatable.lambda.lens.Lens; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Function; + +import static com.jnape.palatable.lambda.lens.Lens.simpleLens; + +/** + * Lenses that operate on {@link Set}s. + */ +public final class SetLens { + + private SetLens() { + } + + /** + * A lens that focuses on whether a {@link Set} contains some value a. Note that copyFn is + * used to avoid mutating the {@link Set} in question. + * + * @param copyFn the copy function + * @param a the value in question + * @param the value type + * @param the set to focus on + * @return a lens that focuses on a value's inclusion in a given {@link Set} + */ + public static > Lens.Simple contains( + Function copyFn, A a) { + return simpleLens(setA -> setA.contains(a), + (setA, include) -> { + SetA copy = copyFn.apply(setA); + if (include) copy.add(a); + else copy.remove(a); + return copy; + }); + } + + /** + * A lens that focuses on whether a {@link Set} contains some value a. Like {@link #contains(Function, + * Object)} but with an implicit copy function that produces {@link HashSet}s. + * + * @param a the value in question + * @param the value type + * @return a lens that focuses on a value's inclusion in a given {@link Set} + */ + public static Lens.Simple, Boolean> contains(A a) { + return contains(HashSet::new, a); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/lens/lenses/SetLensTest.java b/src/test/java/com/jnape/palatable/lambda/lens/lenses/SetLensTest.java new file mode 100644 index 000000000..9421e50f6 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/lens/lenses/SetLensTest.java @@ -0,0 +1,41 @@ +package com.jnape.palatable.lambda.lens.lenses; + +import org.junit.Test; + +import java.util.HashSet; +import java.util.TreeSet; + +import static com.jnape.palatable.lambda.lens.functions.Set.set; +import static java.util.Arrays.asList; +import static java.util.Collections.emptySet; +import static java.util.Collections.singleton; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.junit.Assert.assertThat; +import static testsupport.assertion.LensAssert.assertLensLawfulness; + +public class SetLensTest { + + @Test + public void containsWithCopyFn() { + assertLensLawfulness(SetLens.contains(HashSet::new, 1), + asList(emptySet(), + singleton(1), + singleton(2), + new HashSet<>(asList(1, 2)), + new HashSet<>(asList(2, 3))), + asList(true, false)); + assertThat(set(SetLens.contains(TreeSet::new, 1), true, emptySet()), instanceOf(TreeSet.class)); + } + + @Test + public void containsWithoutCopyFn() { + assertLensLawfulness(SetLens.contains(1), + asList(emptySet(), + singleton(1), + singleton(2), + new HashSet<>(asList(1, 2)), + new HashSet<>(asList(2, 3))), + asList(true, false)); + assertThat(set(SetLens.contains(1), true, emptySet()), instanceOf(HashSet.class)); + } +} \ No newline at end of file From 3da90978f1d756611ab5a4e0a8eced703f9a9f40 Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 13 May 2018 14:10:49 -0500 Subject: [PATCH 08/14] RecursiveResult#invert returns a new RecursiveResult --- CHANGELOG.md | 1 + .../lambda/functions/recursion/RecursiveResult.java | 5 +++++ .../lambda/functions/recursion/RecursiveResultTest.java | 3 +-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6a8e835b..277bc13e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] ### Changed - `ToMap` accepts an `Iterable` covariant in `Map.Entry` +- `RecursiveResult#invert` is also a `RecursiveResult` ### Added - `Upcast` for safely casting up a type hierarchy diff --git a/src/main/java/com/jnape/palatable/lambda/functions/recursion/RecursiveResult.java b/src/main/java/com/jnape/palatable/lambda/functions/recursion/RecursiveResult.java index 555af77e5..7da780a1e 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/recursion/RecursiveResult.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/recursion/RecursiveResult.java @@ -23,6 +23,11 @@ public abstract class RecursiveResult implements CoProduct2 invert() { + return match(RecursiveResult::terminate, RecursiveResult::recurse); + } + @Override @SuppressWarnings("unchecked") public RecursiveResult biMapL(Function fn) { diff --git a/src/test/java/com/jnape/palatable/lambda/functions/recursion/RecursiveResultTest.java b/src/test/java/com/jnape/palatable/lambda/functions/recursion/RecursiveResultTest.java index 49b086466..b46f91f5d 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/recursion/RecursiveResultTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/recursion/RecursiveResultTest.java @@ -1,6 +1,5 @@ package com.jnape.palatable.lambda.functions.recursion; -import com.jnape.palatable.lambda.functions.recursion.RecursiveResult; import com.jnape.palatable.traitor.annotations.TestTraits; import com.jnape.palatable.traitor.framework.Subjects; import com.jnape.palatable.traitor.runners.Traits; @@ -10,9 +9,9 @@ import testsupport.traits.MonadLaws; import testsupport.traits.TraversableLaws; -import static com.jnape.palatable.traitor.framework.Subjects.subjects; 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.traitor.framework.Subjects.subjects; @RunWith(Traits.class) public class RecursiveResultTest { From 85a79ef1ce56687fa8012b3f560be492f464c469 Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 13 May 2018 14:33:55 -0500 Subject: [PATCH 09/14] Short-circuiting in relevant monoids --- CHANGELOG.md | 2 ++ .../jnape/palatable/lambda/monoid/Monoid.java | 18 ++++++++++++++++++ .../palatable/lambda/monoid/builtin/And.java | 9 +++++++++ .../palatable/lambda/monoid/builtin/First.java | 7 +++++++ .../palatable/lambda/monoid/builtin/Or.java | 8 ++++++++ .../lambda/monoid/builtin/AndTest.java | 14 ++++++++++++++ .../lambda/monoid/builtin/FirstTest.java | 14 ++++++++++++++ .../lambda/monoid/builtin/OrTest.java | 14 ++++++++++++++ 8 files changed, 86 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 277bc13e6..8b7f3c547 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ### Changed - `ToMap` accepts an `Iterable` covariant in `Map.Entry` - `RecursiveResult#invert` is also a `RecursiveResult` +- `First`/`And`/`Or` monoids all utilize short-circuiting +- `Monoid#foldLeft/foldRight` delegate to `Monoid#reduceLeft/reduceRight`, respectively ### Added - `Upcast` for safely casting up a type hierarchy diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/Monoid.java b/src/main/java/com/jnape/palatable/lambda/monoid/Monoid.java index 9a2d9f266..1a18a2f8a 100644 --- a/src/main/java/com/jnape/palatable/lambda/monoid/Monoid.java +++ b/src/main/java/com/jnape/palatable/lambda/monoid/Monoid.java @@ -8,7 +8,9 @@ import java.util.function.Function; import java.util.function.Supplier; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Cons.cons; import static com.jnape.palatable.lambda.functions.builtin.fn2.Map.map; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Snoc.snoc; /** * A {@link Monoid} is the pairing of a {@link Semigroup} with an identity element. @@ -64,6 +66,22 @@ default A foldMap(Function fn, Iterable bs) { return reduceLeft(map(fn, bs)); } + /** + * {@inheritDoc} + */ + @Override + default A foldLeft(A a, Iterable as) { + return reduceLeft(cons(a, as)); + } + + /** + * {@inheritDoc} + */ + @Override + default A foldRight(A a, Iterable as) { + return reduceRight(snoc(a, as)); + } + /** * {@inheritDoc} */ diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/And.java b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/And.java index 3b7610510..402663d91 100644 --- a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/And.java +++ b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/And.java @@ -4,6 +4,10 @@ import com.jnape.palatable.lambda.functions.specialized.BiPredicate; import com.jnape.palatable.lambda.monoid.Monoid; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Not.not; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Find.find; + /** * A {@link Monoid} instance formed by Boolean. Equivalent to logical &&. * @@ -27,6 +31,11 @@ public Boolean apply(Boolean x, Boolean y) { return x && y; } + @Override + public Boolean reduceLeft(Iterable bools) { + return find(not(id()), bools).orElse(true); + } + @Override public boolean test(Boolean x, Boolean y) { return apply(x, y); diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/First.java b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/First.java index a933c86a8..97028b692 100644 --- a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/First.java +++ b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/First.java @@ -5,6 +5,8 @@ import com.jnape.palatable.lambda.monoid.Monoid; import static com.jnape.palatable.lambda.adt.Maybe.nothing; +import static com.jnape.palatable.lambda.functions.builtin.fn1.CatMaybes.catMaybes; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Head.head; /** * A {@link Monoid} instance formed by {@link Maybe}<A>. The application to two {@link Maybe} values @@ -33,6 +35,11 @@ public Maybe apply(Maybe x, Maybe y) { return x.fmap(Maybe::just).orElse(y); } + @Override + public Maybe reduceLeft(Iterable> maybes) { + return head(catMaybes(maybes)); + } + @SuppressWarnings("unchecked") public static First first() { return INSTANCE; diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Or.java b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Or.java index e4bc56b8c..68d0813a8 100644 --- a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Or.java +++ b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Or.java @@ -4,6 +4,9 @@ import com.jnape.palatable.lambda.functions.specialized.BiPredicate; import com.jnape.palatable.lambda.monoid.Monoid; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Find.find; + /** * A {@link Monoid} instance formed by Boolean. Equivalent to logical ||. * @@ -32,6 +35,11 @@ public boolean test(Boolean x, Boolean y) { return apply(x, y); } + @Override + public Boolean reduceLeft(Iterable bools) { + return find(id(), bools).orElse(false); + } + @Override public Or flip() { return this; diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/AndTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/AndTest.java index 8ae51c840..d78aaf702 100644 --- a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/AndTest.java +++ b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/AndTest.java @@ -2,6 +2,9 @@ import org.junit.Test; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Cons.cons; import static com.jnape.palatable.lambda.monoid.builtin.And.and; import static org.junit.Assert.assertEquals; @@ -20,4 +23,15 @@ public void monoid() { assertEquals(false, and.apply(true, false)); assertEquals(false, and.apply(false, false)); } + + @Test(timeout = 500) + public void shortCircuiting() { + Iterable bools = cons(false, repeat(true)); + And and = and(); + + assertEquals(false, and.foldLeft(false, bools)); + assertEquals(false, and.foldLeft(true, bools)); + assertEquals(false, and.reduceLeft(bools)); + assertEquals(false, and.foldMap(id(), bools)); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/FirstTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/FirstTest.java index 4851243ba..76675e52e 100644 --- a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/FirstTest.java +++ b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/FirstTest.java @@ -1,9 +1,12 @@ package com.jnape.palatable.lambda.monoid.builtin; +import com.jnape.palatable.lambda.adt.Maybe; import org.junit.Test; import static com.jnape.palatable.lambda.adt.Maybe.just; import static com.jnape.palatable.lambda.adt.Maybe.nothing; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat; import static com.jnape.palatable.lambda.monoid.builtin.First.first; import static org.junit.Assert.assertEquals; @@ -22,4 +25,15 @@ public void monoid() { assertEquals(just(2), first.apply(nothing(), just(2))); assertEquals(nothing(), first.apply(nothing(), nothing())); } + + @Test(timeout = 500) + public void shortCircuiting() { + Iterable> maybeInts = repeat(just(1)); + First first = First.first(); + + assertEquals(just(1), first.foldLeft(nothing(), maybeInts)); + assertEquals(just(1), first.foldLeft(just(1), maybeInts)); + assertEquals(just(1), first.reduceLeft(maybeInts)); + assertEquals(just(1), first.foldMap(id(), maybeInts)); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/OrTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/OrTest.java index 336a98e4d..d421c1e5d 100644 --- a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/OrTest.java +++ b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/OrTest.java @@ -2,6 +2,9 @@ import org.junit.Test; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Cons.cons; import static com.jnape.palatable.lambda.monoid.builtin.Or.or; import static org.junit.Assert.assertEquals; @@ -20,4 +23,15 @@ public void monoid() { assertEquals(true, or.apply(false, true)); assertEquals(false, or.apply(false, false)); } + + @Test(timeout = 500) + public void shortCircuiting() { + Iterable bools = cons(true, repeat(false)); + Or or = or(); + + assertEquals(true, or.foldLeft(false, bools)); + assertEquals(true, or.foldLeft(true, bools)); + assertEquals(true, or.reduceLeft(bools)); + assertEquals(true, or.foldMap(id(), bools)); + } } \ No newline at end of file From 172c8d6abc0920cc57981f2498af33f7df01267c Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 13 May 2018 14:38:00 -0500 Subject: [PATCH 10/14] Housekeeping --- .../palatable/lambda/functions/builtin/fn2/GroupBy.java | 6 +++--- .../jnape/palatable/lambda/functions/builtin/fn2/Slide.java | 2 +- .../jnape/palatable/lambda/functions/builtin/fn3/Times.java | 2 +- .../jnape/palatable/lambda/iteration/ImmutableQueue.java | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/GroupBy.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/GroupBy.java index 11dc659bc..e2ba773e5 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/GroupBy.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/GroupBy.java @@ -12,8 +12,8 @@ import static com.jnape.palatable.lambda.functions.builtin.fn3.FoldLeft.foldLeft; /** - * Given an Iterable<V> vs and a key function V -> K f, fold - * vs into a Map<K, List<V>> by applying f to each element of + * Given an Iterable<V> vs and a key function V -> K f, + * fold vs into a Map<K, List<V>> by applying f to each element of * vs, retaining values that map to the same key in a list, in the order they were iterated in. * * @param the Map key type @@ -32,7 +32,7 @@ public Map> apply(Function keyFn, Iterable return foldLeft((m, v) -> { m.computeIfAbsent(keyFn.apply(v), __ -> new ArrayList<>()).add(v); return m; - }, new HashMap>(), vs); + }, new HashMap<>(), vs); } @SuppressWarnings("unchecked") diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Slide.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Slide.java index 69e1ec16e..337d59788 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Slide.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Slide.java @@ -14,7 +14,7 @@ * Iterable} by one element at a time, returning an {@link Iterable}<{@link Iterable}<A>>. *

* Example: - *

+ * * slide(2, asList(1, 2, 3, 4, 5)); // [[1, 2], [2, 3], [3, 4], [4, 5]] * * @param the Iterable element type diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/Times.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/Times.java index 2227c584e..cdf33a8d7 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/Times.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/Times.java @@ -15,7 +15,7 @@ * n times, returning the result. *

* Example: - *

+ * * times(3, x -> x + 1, 0); // 3 * * @param the input and output type diff --git a/src/main/java/com/jnape/palatable/lambda/iteration/ImmutableQueue.java b/src/main/java/com/jnape/palatable/lambda/iteration/ImmutableQueue.java index 94d94d0b0..5a734a88c 100644 --- a/src/main/java/com/jnape/palatable/lambda/iteration/ImmutableQueue.java +++ b/src/main/java/com/jnape/palatable/lambda/iteration/ImmutableQueue.java @@ -112,7 +112,7 @@ ImmutableQueue tail() { if (!outTail.isEmpty()) return new NonEmpty<>(outTail, inbound); - ImmutableStack newOutbound = foldLeft(ImmutableStack::push, ImmutableStack.empty(), inbound); + ImmutableStack newOutbound = foldLeft(ImmutableStack::push, ImmutableStack.empty(), inbound); return newOutbound.isEmpty() ? empty() : new NonEmpty<>(newOutbound, ImmutableStack.empty()); } } From 4e93dc76f28126136c41d4d5e90e916952ccd6eb Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 13 May 2018 14:41:20 -0500 Subject: [PATCH 11/14] Updating copyright in LICENSE and description in pom --- LICENSE | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 89c5bf75e..b35067105 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013 palatable +Copyright (c) 2018 John Napier (jnape) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/pom.xml b/pom.xml index 75fbb10f8..ff6fc58e4 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ Lambda - Functional patterns for Java 8 + Functional patterns for Java http://www.github.com/palatable/lambda From 4f9304704a3d8e9cc2710f50cef9504ccc16fb16 Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 13 May 2018 16:50:33 -0500 Subject: [PATCH 12/14] Adding ToArray for conveniently creating an A[] from Iterable --- CHANGELOG.md | 1 + .../lambda/functions/builtin/fn2/ToArray.java | 50 +++++++++++++++++++ .../functions/builtin/fn2/ToArrayTest.java | 50 +++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToArray.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToArrayTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b7f3c547..296107a18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ### Added - `Upcast` for safely casting up a type hierarchy - `SetLens`, lenses operating on `Set`s +- `ToArray`, for converting `Iterable` to `A[]` ## [3.0.0] - 2018-05-04 ### Changed diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToArray.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToArray.java new file mode 100644 index 000000000..e12ae85b1 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToArray.java @@ -0,0 +1,50 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.Fn2; + +import java.lang.reflect.Array; +import java.util.Collection; + +import static com.jnape.palatable.lambda.functions.builtin.fn1.Size.size; + +/** + * Write all the elements of an {@link Iterable} directly into an array of the specified type. If the {@link Iterable} + * is an instance of {@link Collection}, use {@link Collection#toArray(Object[])}. + * + * @param the {@link Iterable} element type + */ +public final class ToArray implements Fn2, Iterable, A[]> { + + private static final ToArray INSTANCE = new ToArray<>(); + + private ToArray() { + } + + @Override + @SuppressWarnings("unchecked") + public A[] apply(Class arrayType, Iterable as) { + A[] array = (A[]) Array.newInstance(arrayType.getComponentType(), size(as).intValue()); + if (as instanceof Collection) + return ((Collection) as).toArray(array); + + int index = 0; + for (A a : as) { + array[index++] = a; + } + return array; + } + + @SuppressWarnings("unchecked") + public static ToArray toArray() { + return INSTANCE; + } + + public static Fn1, A[]> toArray(Class arrayType) { + return ToArray.toArray().apply(arrayType); + } + + public static A[] toArray(Class arrayType, Iterable as) { + return toArray(arrayType).apply(as); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToArrayTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToArrayTest.java new file mode 100644 index 000000000..0c4b3001e --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToArrayTest.java @@ -0,0 +1,50 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import org.junit.Test; + +import java.util.AbstractCollection; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import static com.jnape.palatable.lambda.functions.builtin.fn2.ToArray.toArray; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyIterator; +import static org.junit.Assert.assertArrayEquals; + +public class ToArrayTest { + + @Test + public void writesIterableToArray() { + assertArrayEquals(new Integer[]{1, 2, 3}, toArray(Integer[].class, asList(1, 2, 3))); + + List variance = asList(1, 2, 3); + assertArrayEquals(new Object[]{1, 2, 3}, toArray(Object[].class, variance)); + } + + @Test + public void usesCollectionToArrayIfPossible() { + Object sentinel = new Object(); + class CustomCollection extends AbstractCollection { + @Override + public Iterator iterator() { + return emptyIterator(); + } + + @Override + public int size() { + return 0; + } + + @Override + @SuppressWarnings("unchecked") + public T[] toArray(T[] a) { + T[] result = Arrays.copyOf(a, 1); + result[0] = (T) sentinel; + return result; + } + } + + assertArrayEquals(new Object[]{sentinel}, toArray(Object[].class, new CustomCollection())); + } +} \ No newline at end of file From fa6261ea51fddbd5f190417d7c9a239531d87794 Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 13 May 2018 16:58:55 -0500 Subject: [PATCH 13/14] Adding more javadocs to MapLens --- .../jnape/palatable/lambda/lens/lenses/MapLens.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/lens/lenses/MapLens.java b/src/main/java/com/jnape/palatable/lambda/lens/lenses/MapLens.java index f412c1161..6da58926e 100644 --- a/src/main/java/com/jnape/palatable/lambda/lens/lenses/MapLens.java +++ b/src/main/java/com/jnape/palatable/lambda/lens/lenses/MapLens.java @@ -128,6 +128,9 @@ public static Lens.Simple, Collection> values() { /** * A lens that focuses on the inverse of a map (keys and values swapped). In the case of multiple equal values * becoming keys, the last one wins. + *

+ * Note that this lens is very likely to NOT be lawful, since "you get back what you put in" will fail for any keys + * that map to the same value. * * @param the key type * @param the value type @@ -149,9 +152,11 @@ public static Lens.Simple, Map> inverted() { /** * A lens that focuses on a map while mapping its values with the mapping function. *

- * Note that this lens is NOT lawful, since "you get back what you put in" fails for all values B that - * do not map from the current values in S (new mappings cannot be preserved as the inversion of - * fn is not known). + * Note that this lens is very likely to NOT be lawful, since "you get back what you put in" will fail for all + * values B that do not map from the current values in S (new mappings cannot be + * preserved as the inversion of fn is not known). Furthermore, if fn is injective + * (multiple Vs map to the same V2), this lens will also not be lawful for similar reasons + * as stated above. * * @param fn the mapping function * @param the key type @@ -183,7 +188,7 @@ public static Lens.Simple, Map> mappingValues(Functi * @param iso the mapping {@link Iso} * @param the key type * @param the unfocused map value type - * @param the focused map value types + * @param the focused map value type * @return a lens that focuses on a map while mapping its values */ public static Lens.Simple, Map> mappingValues(Iso iso) { From 456092763c87e0b898bc8373f4f775c2f9217e3e Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 13 May 2018 17:18:26 -0500 Subject: [PATCH 14/14] [maven-release-plugin] prepare release lambda-3.0.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ff6fc58e4..21483d5fe 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ lambda - 3.0.1-SNAPSHOT + 3.0.1 jar Lambda