From 352d0948b97977e283441a15641857556ff1503f Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 27 Nov 2016 14:23:48 -0600 Subject: [PATCH 1/8] [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 be94bb8ae..0a32ad524 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ lambda - 1.5.4 + 1.5.5-SNAPSHOT jar Lambda From 71ee383b91f1b98131572b6b7b32a6ad4af89dad Mon Sep 17 00:00:00 2001 From: jnape Date: Thu, 1 Dec 2016 22:48:14 -0600 Subject: [PATCH 2/8] Adding #project for coproducts --- .../lambda/adt/coproduct/CoProduct2.java | 15 +++++++++++++++ .../lambda/adt/coproduct/CoProduct3.java | 17 ++++++++++++++++- .../lambda/adt/coproduct/CoProduct4.java | 17 +++++++++++++++++ .../lambda/adt/coproduct/CoProduct5.java | 18 ++++++++++++++++++ .../lambda/adt/coproduct/CoProduct2Test.java | 9 +++++++++ .../lambda/adt/coproduct/CoProduct3Test.java | 10 ++++++++++ .../lambda/adt/coproduct/CoProduct4Test.java | 11 +++++++++++ .../lambda/adt/coproduct/CoProduct5Test.java | 13 +++++++++++++ 8 files changed, 109 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2.java b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2.java index c01d50773..ed217634d 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2.java @@ -1,11 +1,15 @@ package com.jnape.palatable.lambda.adt.coproduct; +import com.jnape.palatable.lambda.adt.hlist.Tuple2; import com.jnape.palatable.lambda.functor.Bifunctor; import com.jnape.palatable.lambda.functor.Functor; import java.util.Objects; +import java.util.Optional; import java.util.function.Function; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; + /** * A generalization of the coproduct of two types A and B. Coproducts represent the disjoint * union of two or more distinct types, and provides an interface for specifying morphisms from those types to a common @@ -58,6 +62,17 @@ default CoProduct3 diverge() { return match(CoProduct3::a, CoProduct3::b); } + /** + * Project this coproduct onto a tuple, such that the slot in the tuple that corresponds to this coproduct's value + * is present, while the other slots are absent. + * + * @return a tuple of the coproduct projection + */ + default Tuple2, Optional> project() { + return match(a -> tuple(Optional.of(a), Optional.empty()), + b -> tuple(Optional.empty(), Optional.of(b))); + } + @Override default CoProduct2 fmap(Function fn) { return biMapR(fn); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3.java b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3.java index 2dc66cede..98ff06eb1 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3.java @@ -1,11 +1,15 @@ package com.jnape.palatable.lambda.adt.coproduct; +import com.jnape.palatable.lambda.adt.hlist.Tuple3; import com.jnape.palatable.lambda.functor.Bifunctor; import com.jnape.palatable.lambda.functor.Functor; import java.util.Objects; +import java.util.Optional; import java.util.function.Function; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; + /** * A generalization of the coproduct of three types A, B, and C. * @@ -30,7 +34,6 @@ public interface CoProduct3 extends Functor, Bifunctor { R match(Function aFn, Function bFn, Function cFn); - /** * Diverge this coproduct by introducing another possible type that it could represent. * @@ -42,6 +45,18 @@ default CoProduct4 diverge() { return match(CoProduct4::a, CoProduct4::b, CoProduct4::c); } + /** + * Project this coproduct onto a tuple. + * + * @return a tuple of the coproduct projection + * @see CoProduct2#project() + */ + default Tuple3, Optional, Optional> project() { + return match(a -> tuple(Optional.of(a), Optional.empty(), Optional.empty()), + b -> tuple(Optional.empty(), Optional.of(b), Optional.empty()), + c -> tuple(Optional.empty(), Optional.empty(), Optional.of(c))); + } + @Override default CoProduct3 fmap(Function fn) { return biMapR(fn); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4.java b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4.java index 59f8f0043..1b7d7bebe 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4.java @@ -1,11 +1,15 @@ package com.jnape.palatable.lambda.adt.coproduct; +import com.jnape.palatable.lambda.adt.hlist.Tuple4; import com.jnape.palatable.lambda.functor.Bifunctor; import com.jnape.palatable.lambda.functor.Functor; import java.util.Objects; +import java.util.Optional; import java.util.function.Function; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; + /** * A generalization of the coproduct of four types A, B, C, and D. * @@ -45,6 +49,19 @@ default CoProduct5 diverge() { return match(CoProduct5::a, CoProduct5::b, CoProduct5::c, CoProduct5::d); } + /** + * Project this coproduct onto a tuple. + * + * @return a tuple of the coproduct projection + * @see CoProduct2#project() + */ + default Tuple4, Optional, Optional, Optional> project() { + return match(a -> tuple(Optional.of(a), Optional.empty(), Optional.empty(), Optional.empty()), + b -> tuple(Optional.empty(), Optional.of(b), Optional.empty(), Optional.empty()), + c -> tuple(Optional.empty(), Optional.empty(), Optional.of(c), Optional.empty()), + d -> tuple(Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(d))); + } + @Override default CoProduct4 fmap(Function fn) { return biMapR(fn); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5.java b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5.java index 2594f16df..fdd92e7f4 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5.java @@ -1,11 +1,15 @@ package com.jnape.palatable.lambda.adt.coproduct; +import com.jnape.palatable.lambda.adt.hlist.Tuple5; import com.jnape.palatable.lambda.functor.Bifunctor; import com.jnape.palatable.lambda.functor.Functor; import java.util.Objects; +import java.util.Optional; import java.util.function.Function; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; + /** * A generalization of the coproduct of five types A, B, C, D, and * E. @@ -38,6 +42,20 @@ R match(Function aFn, Function dFn, Function eFn); + /** + * Project this coproduct onto a tuple. + * + * @return a tuple of the coproduct projection + * @see CoProduct2#project() + */ + default Tuple5, Optional, Optional, Optional, Optional> project() { + return match(a -> tuple(Optional.of(a), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()), + b -> tuple(Optional.empty(), Optional.of(b), Optional.empty(), Optional.empty(), Optional.empty()), + c -> tuple(Optional.empty(), Optional.empty(), Optional.of(c), Optional.empty(), Optional.empty()), + d -> tuple(Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(d), Optional.empty()), + e -> tuple(Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(e))); + } + @Override default CoProduct5 fmap(Function fn) { return biMapR(fn); diff --git a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2Test.java b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2Test.java index f056ae9f7..9c96b0de2 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2Test.java @@ -3,8 +3,11 @@ import org.junit.Before; import org.junit.Test; +import java.util.Optional; + import static com.jnape.palatable.lambda.adt.coproduct.CoProduct2.a; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct2.b; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; import static org.junit.Assert.assertEquals; @@ -31,6 +34,12 @@ public void diverge() { assertEquals(CoProduct3.b(true), b.diverge()); } + @Test + public void project() { + assertEquals(tuple(Optional.of(1), Optional.empty()), CoProduct2.a(1).project()); + assertEquals(tuple(Optional.empty(), Optional.of("b")), CoProduct2.b("b").project()); + } + @Test public void functorProperties() { assertEquals(a, a.fmap(bool -> !bool)); diff --git a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3Test.java b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3Test.java index 799b641e5..ef696232b 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3Test.java @@ -3,9 +3,12 @@ import org.junit.Before; import org.junit.Test; +import java.util.Optional; + import static com.jnape.palatable.lambda.adt.coproduct.CoProduct3.a; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct3.b; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct3.c; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; import static org.junit.Assert.assertEquals; @@ -36,6 +39,13 @@ public void diverge() { assertEquals(CoProduct4.c(true), c.diverge()); } + @Test + public void project() { + assertEquals(tuple(Optional.of(1), Optional.empty(), Optional.empty()), CoProduct3.a(1).project()); + assertEquals(tuple(Optional.empty(), Optional.of("b"), Optional.empty()), CoProduct3.b("b").project()); + assertEquals(tuple(Optional.empty(), Optional.empty(), Optional.of('c')), CoProduct3.c('c').project()); + } + @Test public void functorProperties() { assertEquals(a, a.fmap(bool -> !bool)); diff --git a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4Test.java b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4Test.java index 31ac2dd01..e1c6c8783 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4Test.java @@ -3,10 +3,13 @@ import org.junit.Before; import org.junit.Test; +import java.util.Optional; + import static com.jnape.palatable.lambda.adt.coproduct.CoProduct4.a; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct4.b; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct4.c; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct4.d; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; import static org.junit.Assert.assertEquals; @@ -41,6 +44,14 @@ public void diverge() { assertEquals(CoProduct5.d(4D), d.diverge()); } + @Test + public void project() { + assertEquals(tuple(Optional.of(1), Optional.empty(), Optional.empty(), Optional.empty()), CoProduct4.a(1).project()); + assertEquals(tuple(Optional.empty(), Optional.of("b"), Optional.empty(), Optional.empty()), CoProduct4.b("b").project()); + assertEquals(tuple(Optional.empty(), Optional.empty(), Optional.of('c'), Optional.empty()), CoProduct4.c('c').project()); + assertEquals(tuple(Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(4L)), CoProduct4.d(4L).project()); + } + @Test public void functorProperties() { assertEquals(a, a.fmap(d -> -d)); diff --git a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5Test.java b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5Test.java index 6adb56d29..eddc7e56f 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5Test.java @@ -3,11 +3,14 @@ import org.junit.Before; import org.junit.Test; +import java.util.Optional; + import static com.jnape.palatable.lambda.adt.coproduct.CoProduct5.a; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct5.b; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct5.c; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct5.d; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct5.e; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; import static org.junit.Assert.assertEquals; @@ -37,6 +40,16 @@ public void match() { assertEquals('z', e.match(id(), id(), id(), id(), id())); } + @Test + public void project() { + assertEquals(tuple(Optional.of(1), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()), CoProduct5.a(1).project()); + assertEquals(tuple(Optional.empty(), Optional.of("b"), Optional.empty(), Optional.empty(), Optional.empty()), CoProduct5.b("b").project()); + assertEquals(tuple(Optional.empty(), Optional.empty(), Optional.of('c'), Optional.empty(), Optional.empty()), CoProduct5.c('c').project()); + assertEquals(tuple(Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(4L), Optional.empty()), CoProduct5.d(4L).project()); + assertEquals(tuple(Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(false)), CoProduct5.e(false).project()); + } + + @Test public void functorProperties() { assertEquals(a, a.fmap(Character::toUpperCase)); From 108f6312d6d5c7dac88226f4168117d9fa2c1914 Mon Sep 17 00:00:00 2001 From: jnape Date: Fri, 9 Dec 2016 18:03:08 -0600 Subject: [PATCH 3/8] Eq partialed static factory should be typed to Predicate --- .../com/jnape/palatable/lambda/functions/builtin/fn2/Eq.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Eq.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Eq.java index 7511a6490..b0f7bd9a0 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Eq.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Eq.java @@ -1,7 +1,7 @@ package com.jnape.palatable.lambda.functions.builtin.fn2; -import com.jnape.palatable.lambda.functions.Fn1; import com.jnape.palatable.lambda.functions.specialized.BiPredicate; +import com.jnape.palatable.lambda.functions.specialized.Predicate; /** * Type-safe equality in function form; uses {@link Object#equals}, not ==. @@ -22,7 +22,7 @@ public static Eq eq() { return new Eq<>(); } - public static Fn1 eq(A x) { + public static Predicate eq(A x) { return Eq.eq().apply(x); } From f00ea5a471e43932ad7936503e11a05859ed0b6f Mon Sep 17 00:00:00 2001 From: jnape Date: Mon, 12 Dec 2016 17:38:36 -0600 Subject: [PATCH 4/8] Adding toCollection --- .../functions/builtin/fn1/ToCollection.java | 43 +++++++++++++++++++ .../builtin/fn1/ToCollectionTest.java | 20 +++++++++ 2 files changed, 63 insertions(+) create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/ToCollection.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/ToCollectionTest.java diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/ToCollection.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/ToCollection.java new file mode 100644 index 000000000..6f5f803ff --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/ToCollection.java @@ -0,0 +1,43 @@ +package com.jnape.palatable.lambda.functions.builtin.fn1; + +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.Fn2; + +import java.util.Collection; +import java.util.function.Supplier; + +/** + * Given a {@link Supplier} of some {@link Collection} C, create an instance of C and add + * all of the elements in the provided Iterable to the instance. Note that instances of C + * must support {@link Collection#add} (which is to say, must not throw on invocation). + * + * @param the iterable element type + * @param the resulting collection type + */ +public final class ToCollection> implements Fn2, Iterable, C> { + + private static final ToCollection INSTANCE = new ToCollection(); + + private ToCollection() { + } + + @Override + public C apply(Supplier cSupplier, Iterable as) { + C c = cSupplier.get(); + as.forEach(c::add); + return c; + } + + @SuppressWarnings("unchecked") + public static > ToCollection toCollection() { + return INSTANCE; + } + + public static > Fn1, C> toCollection(Supplier cSupplier) { + return ToCollection.toCollection().apply(cSupplier); + } + + public static > C toCollection(Supplier cSupplier, Iterable as) { + return toCollection(cSupplier).apply(as); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/ToCollectionTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/ToCollectionTest.java new file mode 100644 index 000000000..8593e4863 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/ToCollectionTest.java @@ -0,0 +1,20 @@ +package com.jnape.palatable.lambda.functions.builtin.fn1; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.function.Supplier; + +import static com.jnape.palatable.lambda.functions.builtin.fn1.ToCollection.toCollection; +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; + +public class ToCollectionTest { + + @Test + public void convertsIterablesToCollectionInstance() { + Supplier> listFactory = ArrayList::new; + assertEquals(asList(1, 2, 3), toCollection(listFactory, asList(1, 2, 3))); + } +} \ No newline at end of file From c9fd01394f609e46a632b84345a714ff0fa63387 Mon Sep 17 00:00:00 2001 From: jnape Date: Mon, 12 Dec 2016 22:08:36 -0600 Subject: [PATCH 5/8] Adding size --- .../lambda/functions/builtin/fn1/Size.java | 33 +++++++++++++++++++ .../functions/builtin/fn1/SizeTest.java | 33 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Size.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/SizeTest.java diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Size.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Size.java new file mode 100644 index 000000000..95dca3f36 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Size.java @@ -0,0 +1,33 @@ +package com.jnape.palatable.lambda.functions.builtin.fn1; + +import com.jnape.palatable.lambda.functions.Fn1; + +import java.util.Collection; + +public final class Size implements Fn1, Long> { + + private static final Size INSTANCE = new Size(); + + private Size() { + } + + @Override + public Long apply(Iterable iterable) { + if (iterable instanceof Collection) + return (long) ((Collection) iterable).size(); + + long size = 0L; + for (Object ignored : iterable) { + size++; + } + return size; + } + + public static Size size() { + return INSTANCE; + } + + public static Long size(Iterable iterable) { + return size().apply(iterable); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/SizeTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/SizeTest.java new file mode 100644 index 000000000..7ee079443 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/SizeTest.java @@ -0,0 +1,33 @@ +package com.jnape.palatable.lambda.functions.builtin.fn1; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collection; + +import static com.jnape.palatable.lambda.functions.builtin.fn1.Size.size; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +public class SizeTest { + + @Test + public void countsElementsInIterable() { + assertEquals((Long) 0L, size(emptyList())); + assertEquals((Long) 3L, size(asList(1, 2, 3))); + } + + @Test + public void optimizesForCollections() { + Collection collection = spy(new ArrayList() {{ + add(1); + add(2); + add(3); + }}); + when(collection.iterator()).thenThrow(new IllegalStateException("should not be using the iterator")); + assertEquals((Long) 3L, size(collection)); + } +} \ No newline at end of file From 474e8a9ea9eba61e723f8831749e3cfd0bf49e1e Mon Sep 17 00:00:00 2001 From: jnape Date: Sat, 17 Dec 2016 12:53:50 -0600 Subject: [PATCH 6/8] Adding converge for CoProduct3 and higher, as well as type-indexed projections --- .../lambda/adt/coproduct/CoProduct2.java | 34 ++++++-- .../lambda/adt/coproduct/CoProduct3.java | 47 ++++++++++ .../lambda/adt/coproduct/CoProduct4.java | 52 +++++++++++ .../lambda/adt/coproduct/CoProduct5.java | 61 +++++++++++++ .../lambda/adt/coproduct/CoProduct2Test.java | 15 ++-- .../lambda/adt/coproduct/CoProduct3Test.java | 25 ++++-- .../lambda/adt/coproduct/CoProduct4Test.java | 30 +++++-- .../lambda/adt/coproduct/CoProduct5Test.java | 34 ++++++-- .../traits/CoProductProjections.java | 86 +++++++++++++++++++ 9 files changed, 346 insertions(+), 38 deletions(-) create mode 100644 src/test/java/testsupport/traits/CoProductProjections.java diff --git a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2.java b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2.java index ed217634d..66684acde 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2.java @@ -34,14 +34,10 @@ public interface CoProduct2 extends Functor, Bifunctor { R match(Function aFn, Function bFn); /** - * Diverge this coproduct by introducing another possible type that it could represent. - *

- * It's important to understand that this does not alter the essential value represented by this coproduct: if the - * value was an A before divergence, it is still an A; likewise with B. - *

- * The purpose of this operation is to allow the use of a more convergent coproduct with a more divergent one; that - * is, if a CoProduct3<String, Integer, Boolean> is expected, a CoProduct2<String, - * Integer> should suffice. + * Diverge this coproduct by introducing another possible type that it could represent. As no morphisms can be + * provided mapping current types to the new type, this operation merely acts as a convenience method to allow the + * use of a more convergent coproduct with a more divergent one; that is, if a CoProduct3<String, Integer, + * Boolean> is expected, a CoProduct2<String, Integer> should suffice. *

* Generally, we use inheritance to make this a non-issue; however, with coproducts of differing magnitudes, we * cannot guarantee variance compatibility in one direction conveniently at construction time, and in the other @@ -56,7 +52,7 @@ public interface CoProduct2 extends Functor, Bifunctor { * single magnitude difference. * * @param the additional possible type of this coproduct - * @return a Coproduct3<A, B, C> + * @return a coproduct of the initial types plus the new type */ default CoProduct3 diverge() { return match(CoProduct3::a, CoProduct3::b); @@ -73,6 +69,26 @@ default Tuple2, Optional> project() { b -> tuple(Optional.empty(), Optional.of(b))); } + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the first slot value. + * + * @return an optional value representing the projection of the "a" type index + */ + @SuppressWarnings("unused") + default Optional projectA() { + return project()._1(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the second slot value. + * + * @return an optional value representing the projection of the "b" type index + */ + @SuppressWarnings("unused") + default Optional projectB() { + return project()._2(); + } + @Override default CoProduct2 fmap(Function fn) { return biMapR(fn); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3.java b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3.java index 98ff06eb1..3b5047c45 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3.java @@ -45,6 +45,23 @@ default CoProduct4 diverge() { return match(CoProduct4::a, CoProduct4::b, CoProduct4::c); } + /** + * Converge this coproduct down to a lower order coproduct by mapping the last possible type into an earlier + * possible type. This is the categorical dual of {@link CoProduct2#diverge}, which introduces the type + * C and raises the order from 2 to 3. + *

+ * The following laws hold for any two coproducts of single order difference: + *

+ * + * @param convergenceFn function from last possible type to earlier type + * @return a coproduct of the initial types without the terminal type + */ + default CoProduct2 converge(Function> convergenceFn) { + return match(CoProduct2::a, CoProduct2::b, convergenceFn); + } + /** * Project this coproduct onto a tuple. * @@ -57,6 +74,36 @@ default Tuple3, Optional, Optional> project() { c -> tuple(Optional.empty(), Optional.empty(), Optional.of(c))); } + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the first slot value. + * + * @return an optional value representing the projection of the "a" type index + */ + @SuppressWarnings("unused") + default Optional projectA() { + return project()._1(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the second slot value. + * + * @return an optional value representing the projection of the "b" type index + */ + @SuppressWarnings("unused") + default Optional projectB() { + return project()._2(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the third slot value. + * + * @return an optional value representing the projection of the "c" type index + */ + @SuppressWarnings("unused") + default Optional projectC() { + return project()._3(); + } + @Override default CoProduct3 fmap(Function fn) { return biMapR(fn); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4.java b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4.java index 1b7d7bebe..466a43509 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4.java @@ -49,6 +49,18 @@ default CoProduct5 diverge() { return match(CoProduct5::a, CoProduct5::b, CoProduct5::c, CoProduct5::d); } + /** + * Converge this coproduct down to a lower order coproduct by mapping the last possible type into an earlier + * possible type. + * + * @param convergenceFn function from last possible type to earlier type + * @return a coproduct of the initial types without the terminal type + * @see CoProduct3#converge + */ + default CoProduct3 converge(Function> convergenceFn) { + return match(CoProduct3::a, CoProduct3::b, CoProduct3::c, convergenceFn); + } + /** * Project this coproduct onto a tuple. * @@ -62,6 +74,46 @@ default Tuple4, Optional, Optional, Optional> project() { d -> tuple(Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(d))); } + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the first slot value. + * + * @return an optional value representing the projection of the "a" type index + */ + @SuppressWarnings("unused") + default Optional projectA() { + return project()._1(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the second slot value. + * + * @return an optional value representing the projection of the "b" type index + */ + @SuppressWarnings("unused") + default Optional projectB() { + return project()._2(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the third slot value. + * + * @return an optional value representing the projection of the "c" type index + */ + @SuppressWarnings("unused") + default Optional projectC() { + return project()._3(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the fourth slot value. + * + * @return an optional value representing the projection of the "d" type index + */ + @SuppressWarnings("unused") + default Optional projectD() { + return project()._4(); + } + @Override default CoProduct4 fmap(Function fn) { return biMapR(fn); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5.java b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5.java index fdd92e7f4..a6d46ab49 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5.java @@ -42,6 +42,17 @@ R match(Function aFn, Function dFn, Function eFn); + /** + * Converge this coproduct down to a lower order coproduct by mapping the last possible type into an earlier + * possible type. + * + * @param convergenceFn morphism E -> {@link CoProduct4}<A, B, C, D> + * @return a CoProduct4<A, B, C, D> + */ + default CoProduct4 converge(Function> convergenceFn) { + return match(CoProduct4::a, CoProduct4::b, CoProduct4::c, CoProduct4::d, convergenceFn); + } + /** * Project this coproduct onto a tuple. * @@ -56,6 +67,56 @@ default Tuple5, Optional, Optional, Optional, Optional> e -> tuple(Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(e))); } + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the first slot value. + * + * @return an optional value representing the projection of the "a" type index + */ + @SuppressWarnings("unused") + default Optional projectA() { + return project()._1(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the second slot value. + * + * @return an optional value representing the projection of the "b" type index + */ + @SuppressWarnings("unused") + default Optional projectB() { + return project()._2(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the third slot value. + * + * @return an optional value representing the projection of the "c" type index + */ + @SuppressWarnings("unused") + default Optional projectC() { + return project()._3(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the fourth slot value. + * + * @return an optional value representing the projection of the "d" type index + */ + @SuppressWarnings("unused") + default Optional projectD() { + return project()._4(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the fifth slot value. + * + * @return an optional value representing the projection of the "e" type index + */ + @SuppressWarnings("unused") + default Optional projectE() { + return project()._5(); + } + @Override default CoProduct5 fmap(Function fn) { return biMapR(fn); diff --git a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2Test.java b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2Test.java index 9c96b0de2..af5b2ef53 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2Test.java @@ -1,16 +1,18 @@ package com.jnape.palatable.lambda.adt.coproduct; +import com.jnape.palatable.traitor.annotations.TestTraits; +import com.jnape.palatable.traitor.runners.Traits; import org.junit.Before; import org.junit.Test; - -import java.util.Optional; +import org.junit.runner.RunWith; +import testsupport.traits.CoProductProjections; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct2.a; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct2.b; -import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; import static org.junit.Assert.assertEquals; +@RunWith(Traits.class) public class CoProduct2Test { private CoProduct2 a; @@ -34,10 +36,9 @@ public void diverge() { assertEquals(CoProduct3.b(true), b.diverge()); } - @Test - public void project() { - assertEquals(tuple(Optional.of(1), Optional.empty()), CoProduct2.a(1).project()); - assertEquals(tuple(Optional.empty(), Optional.of("b")), CoProduct2.b("b").project()); + @TestTraits({CoProductProjections.class}) + public Class projections() { + return CoProduct2.class; } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3Test.java b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3Test.java index ef696232b..48cdfc1bf 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3Test.java @@ -1,17 +1,21 @@ package com.jnape.palatable.lambda.adt.coproduct; +import com.jnape.palatable.traitor.annotations.TestTraits; +import com.jnape.palatable.traitor.runners.Traits; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import testsupport.traits.CoProductProjections; -import java.util.Optional; +import java.util.function.Function; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct3.a; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct3.b; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct3.c; -import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; import static org.junit.Assert.assertEquals; +@RunWith(Traits.class) public class CoProduct3Test { private CoProduct3 a; @@ -19,7 +23,7 @@ public class CoProduct3Test { private CoProduct3 c; @Before - public void setUp() throws Exception { + public void setUp() { a = a(1); b = b("two"); c = c(true); @@ -40,10 +44,17 @@ public void diverge() { } @Test - public void project() { - assertEquals(tuple(Optional.of(1), Optional.empty(), Optional.empty()), CoProduct3.a(1).project()); - assertEquals(tuple(Optional.empty(), Optional.of("b"), Optional.empty()), CoProduct3.b("b").project()); - assertEquals(tuple(Optional.empty(), Optional.empty(), Optional.of('c')), CoProduct3.c('c').project()); + public void converge() { + Function> convergenceFn = x -> x ? CoProduct2.a(1) : CoProduct2.b("false"); + assertEquals(CoProduct2.a(1), a.converge(convergenceFn)); + assertEquals(CoProduct2.b("two"), b.converge(convergenceFn)); + assertEquals(CoProduct2.a(1), CoProduct3.c(true).converge(convergenceFn)); + assertEquals(CoProduct2.b("false"), CoProduct3.c(false).converge(convergenceFn)); + } + + @TestTraits({CoProductProjections.class}) + public Class projections() { + return CoProduct3.class; } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4Test.java b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4Test.java index e1c6c8783..931ac3e94 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4Test.java @@ -1,18 +1,22 @@ package com.jnape.palatable.lambda.adt.coproduct; +import com.jnape.palatable.traitor.annotations.TestTraits; +import com.jnape.palatable.traitor.runners.Traits; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import testsupport.traits.CoProductProjections; -import java.util.Optional; +import java.util.function.Function; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct4.a; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct4.b; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct4.c; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct4.d; -import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; import static org.junit.Assert.assertEquals; +@RunWith(Traits.class) public class CoProduct4Test { private CoProduct4 a; @@ -45,11 +49,23 @@ public void diverge() { } @Test - public void project() { - assertEquals(tuple(Optional.of(1), Optional.empty(), Optional.empty(), Optional.empty()), CoProduct4.a(1).project()); - assertEquals(tuple(Optional.empty(), Optional.of("b"), Optional.empty(), Optional.empty()), CoProduct4.b("b").project()); - assertEquals(tuple(Optional.empty(), Optional.empty(), Optional.of('c'), Optional.empty()), CoProduct4.c('c').project()); - assertEquals(tuple(Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(4L)), CoProduct4.d(4L).project()); + public void converge() { + Function> convergenceFn = x -> x.equals(1d) + ? CoProduct3.a(1) + : x.equals(2d) + ? CoProduct3.b("b") + : CoProduct3.c(false); + assertEquals(CoProduct3.a(1), a.converge(convergenceFn)); + assertEquals(CoProduct3.b("two"), b.converge(convergenceFn)); + assertEquals(CoProduct3.c(true), c.converge(convergenceFn)); + assertEquals(CoProduct3.a(1), CoProduct4.d(1d).converge(convergenceFn)); + assertEquals(CoProduct3.b("b"), CoProduct4.d(2d).converge(convergenceFn)); + assertEquals(CoProduct3.c(false), CoProduct4.d(3d).converge(convergenceFn)); + } + + @TestTraits({CoProductProjections.class}) + public Class projections() { + return CoProduct4.class; } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5Test.java b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5Test.java index eddc7e56f..0e2406d3a 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5Test.java @@ -1,19 +1,23 @@ package com.jnape.palatable.lambda.adt.coproduct; +import com.jnape.palatable.traitor.annotations.TestTraits; +import com.jnape.palatable.traitor.runners.Traits; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import testsupport.traits.CoProductProjections; -import java.util.Optional; +import java.util.function.Function; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct5.a; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct5.b; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct5.c; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct5.d; import static com.jnape.palatable.lambda.adt.coproduct.CoProduct5.e; -import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; import static org.junit.Assert.assertEquals; +@RunWith(Traits.class) public class CoProduct5Test { private CoProduct5 a; @@ -41,14 +45,28 @@ public void match() { } @Test - public void project() { - assertEquals(tuple(Optional.of(1), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()), CoProduct5.a(1).project()); - assertEquals(tuple(Optional.empty(), Optional.of("b"), Optional.empty(), Optional.empty(), Optional.empty()), CoProduct5.b("b").project()); - assertEquals(tuple(Optional.empty(), Optional.empty(), Optional.of('c'), Optional.empty(), Optional.empty()), CoProduct5.c('c').project()); - assertEquals(tuple(Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(4L), Optional.empty()), CoProduct5.d(4L).project()); - assertEquals(tuple(Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(false)), CoProduct5.e(false).project()); + public void converge() { + Function> convergenceFn = x -> x.equals('a') + ? CoProduct4.a(1) + : x.equals('b') + ? CoProduct4.b("b") + : x.equals('c') + ? CoProduct4.c(false) + : CoProduct4.d(1d); + assertEquals(CoProduct4.a(1), a.converge(convergenceFn)); + assertEquals(CoProduct4.b("two"), b.converge(convergenceFn)); + assertEquals(CoProduct4.c(true), c.converge(convergenceFn)); + assertEquals(CoProduct4.d(4D), d.converge(convergenceFn)); + assertEquals(CoProduct4.a(1), CoProduct5.e('a').converge(convergenceFn)); + assertEquals(CoProduct4.b("b"), CoProduct5.e('b').converge(convergenceFn)); + assertEquals(CoProduct4.c(false), CoProduct5.e('c').converge(convergenceFn)); + assertEquals(CoProduct4.d(1d), CoProduct5.e('d').converge(convergenceFn)); } + @TestTraits({CoProductProjections.class}) + public Class projections() { + return CoProduct5.class; + } @Test public void functorProperties() { diff --git a/src/test/java/testsupport/traits/CoProductProjections.java b/src/test/java/testsupport/traits/CoProductProjections.java new file mode 100644 index 000000000..77aa47d98 --- /dev/null +++ b/src/test/java/testsupport/traits/CoProductProjections.java @@ -0,0 +1,86 @@ +package testsupport.traits; + +import com.jnape.palatable.traitor.traits.Trait; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static com.jnape.palatable.lambda.functions.builtin.fn1.Size.size; +import static com.jnape.palatable.lambda.functions.builtin.fn1.ToCollection.toCollection; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Filter.filter; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Map.map; +import static java.lang.String.format; +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public final class CoProductProjections implements Trait> { + + @Override + public void test(Class coProductType) { + List declaredMethods = asList(coProductType.getDeclaredMethods()); + Iterable staticFactoryMethods = filter(m -> m.getName().length() == 1, declaredMethods); + List staticFactoryMethodNames = toCollection(ArrayList::new, map(Method::getName, staticFactoryMethods)); + Collections.sort(staticFactoryMethodNames); + + if (size(staticFactoryMethodNames) == 0) + fail("Couldn't find any static constructors for type " + coProductType.getSimpleName()); + + assertIndividualProjections(coProductType, staticFactoryMethods); + assertFullProjection(coProductType, staticFactoryMethods, staticFactoryMethodNames); + } + + private void assertFullProjection(Class coProductType, Iterable staticFactoryMethods, + List staticFactoryMethodNames) { + staticFactoryMethods.forEach(sfm -> { + try { + String value = "value"; + Object instance = sfm.invoke(null, value); + Method project = coProductType.getDeclaredMethod("project"); + Object tuple = project.invoke(instance); + for (int i = 0; i < staticFactoryMethodNames.size(); i++) { + Object slot = tuple.getClass().getDeclaredMethod("_" + (i + 1)).invoke(tuple); + if (i == staticFactoryMethodNames.indexOf(sfm.getName())) + assertEquals(format("Assertion %1$s.%2$s(\"%3$s\")._%4$s() == Optional.of(\"%3$s\")", + coProductType.getSimpleName(), + sfm.getName(), + value, + i + 1), Optional.of(value), slot); + else + assertEquals(format("Assertion %1$s.%2$s(\"%3$s\")._%4$s() == Optional.empty()", + coProductType.getSimpleName(), + sfm.getName(), + value, + i + 1), Optional.empty(), slot); + } + } catch (IllegalAccessException | InvocationTargetException e) { + fail("Trait " + getClass().getSimpleName() + " is broken: " + e.getMessage()); + } catch (NoSuchMethodException e) { + fail(coProductType.getSimpleName() + " is missing an expected method: " + e.getMessage()); + } + }); + } + + private void assertIndividualProjections(Class coProductType, Iterable staticFactoryMethods) { + staticFactoryMethods.forEach(sfm -> { + try { + String value = "value"; + Object instance = sfm.invoke(null, value); + Method project = coProductType.getDeclaredMethod("project" + sfm.getName().toUpperCase()); + assertEquals(format("Assertion %1$s.%2$s(\"%3$s\").%4$s() == Optional.of(\"%3$s\")", + coProductType.getSimpleName(), + sfm.getName(), + value, + project.getName()), Optional.of(value), project.invoke(instance)); + } catch (IllegalAccessException | InvocationTargetException e) { + fail("Trait " + getClass().getSimpleName() + " is broken: " + e.getMessage()); + } catch (NoSuchMethodException e) { + fail(coProductType.getSimpleName() + " is missing an expected method: " + e.getMessage()); + } + }); + } +} From a950e870d4171d56a21ce0f36571e6382a32da4e Mon Sep 17 00:00:00 2001 From: jnape Date: Wed, 30 Nov 2016 18:28:22 -0600 Subject: [PATCH 7/8] Moving semigroups and monoids under fn2 for better discoverability; various renaming. Adding Monoid/Semigroup factory specializations of Fn3, adding Fn4 for Bi-specializations, and condensing dual semigroup and monoid function types into single classes with specific static factory methods. monoid/semigroups include: - LeftAll - RightAll - LeftAny - RightAny - Concat - Collapse - Merge - Present - PutAll --- .../palatable/lambda/adt/hlist/Tuple2.java | 9 ++ .../jnape/palatable/lambda/functions/Fn2.java | 1 - .../jnape/palatable/lambda/functions/Fn3.java | 1 - .../jnape/palatable/lambda/functions/Fn4.java | 85 +++++++++++++++++++ .../specialized/BiMonoidFactory.java | 26 ++++++ .../specialized/BiSemigroupFactory.java | 32 +++++++ .../functions/specialized/MonoidFactory.java | 14 +++ .../specialized/SemigroupFactory.java | 16 ++++ .../jnape/palatable/lambda/monoid/Monoid.java | 15 ++++ .../lambda/monoid/builtin/Collapse.java | 59 +++++++++++++ .../lambda/monoid/builtin/Concat.java | 50 +++++++++++ .../lambda/monoid/builtin/LeftAll.java | 59 +++++++++++++ .../lambda/monoid/builtin/LeftAny.java | 59 +++++++++++++ .../lambda/monoid/builtin/Merge.java | 58 +++++++++++++ .../lambda/monoid/builtin/Present.java | 58 +++++++++++++ .../lambda/monoid/builtin/PutAll.java | 43 ++++++++++ .../lambda/monoid/builtin/RightAll.java | 56 ++++++++++++ .../lambda/monoid/builtin/RightAny.java | 59 +++++++++++++ .../palatable/lambda/semigroup/Left.java | 35 -------- .../palatable/lambda/semigroup/Present.java | 37 -------- .../palatable/lambda/semigroup/Right.java | 33 ------- .../lambda/semigroup/builtin/Collapse.java | 59 +++++++++++++ .../lambda/semigroup/builtin/Concat.java | 42 +++++++++ .../lambda/semigroup/builtin/LeftAll.java | 59 +++++++++++++ .../lambda/semigroup/builtin/LeftAny.java | 60 +++++++++++++ .../lambda/semigroup/builtin/Merge.java | 55 ++++++++++++ .../lambda/semigroup/builtin/RightAll.java | 58 +++++++++++++ .../lambda/semigroup/builtin/RightAny.java | 61 +++++++++++++ .../palatable/lambda/monoid/MonoidTest.java | 1 + .../lambda/monoid/builtin/CollapseTest.java | 22 +++++ .../lambda/monoid/builtin/ConcatTest.java | 25 ++++++ .../lambda/monoid/builtin/LeftAllTest.java | 24 ++++++ .../lambda/monoid/builtin/LeftAnyTest.java | 24 ++++++ .../lambda/monoid/builtin/MergeTest.java | 38 +++++++++ .../lambda/monoid/builtin/PresentTest.java | 23 +++++ .../lambda/monoid/builtin/PutAllTest.java | 32 +++++++ .../lambda/monoid/builtin/RightAllTest.java | 24 ++++++ .../lambda/monoid/builtin/RightAnyTest.java | 24 ++++++ .../palatable/lambda/semigroup/LeftTest.java | 23 ----- .../lambda/semigroup/PresentTest.java | 24 ------ .../palatable/lambda/semigroup/RightTest.java | 23 ----- .../lambda/semigroup/SemigroupTest.java | 1 + .../semigroup/builtin/CollapseTest.java | 20 +++++ .../lambda/semigroup/builtin/ConcatTest.java | 23 +++++ .../lambda/semigroup/builtin/LeftAllTest.java | 22 +++++ .../lambda/semigroup/builtin/LeftAnyTest.java | 23 +++++ .../lambda/semigroup/builtin/MergeTest.java | 25 ++++++ .../semigroup/builtin/RightAllTest.java | 22 +++++ .../semigroup/builtin/RightAnyTest.java | 23 +++++ 49 files changed, 1488 insertions(+), 177 deletions(-) create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/Fn4.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/specialized/BiMonoidFactory.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/specialized/BiSemigroupFactory.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/specialized/MonoidFactory.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/specialized/SemigroupFactory.java create mode 100644 src/main/java/com/jnape/palatable/lambda/monoid/builtin/Collapse.java create mode 100644 src/main/java/com/jnape/palatable/lambda/monoid/builtin/Concat.java create mode 100644 src/main/java/com/jnape/palatable/lambda/monoid/builtin/LeftAll.java create mode 100644 src/main/java/com/jnape/palatable/lambda/monoid/builtin/LeftAny.java create mode 100644 src/main/java/com/jnape/palatable/lambda/monoid/builtin/Merge.java create mode 100644 src/main/java/com/jnape/palatable/lambda/monoid/builtin/Present.java create mode 100644 src/main/java/com/jnape/palatable/lambda/monoid/builtin/PutAll.java create mode 100644 src/main/java/com/jnape/palatable/lambda/monoid/builtin/RightAll.java create mode 100644 src/main/java/com/jnape/palatable/lambda/monoid/builtin/RightAny.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/semigroup/Left.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/semigroup/Present.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/semigroup/Right.java create mode 100644 src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Collapse.java create mode 100644 src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Concat.java create mode 100644 src/main/java/com/jnape/palatable/lambda/semigroup/builtin/LeftAll.java create mode 100644 src/main/java/com/jnape/palatable/lambda/semigroup/builtin/LeftAny.java create mode 100644 src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Merge.java create mode 100644 src/main/java/com/jnape/palatable/lambda/semigroup/builtin/RightAll.java create mode 100644 src/main/java/com/jnape/palatable/lambda/semigroup/builtin/RightAny.java create mode 100644 src/test/java/com/jnape/palatable/lambda/monoid/builtin/CollapseTest.java create mode 100644 src/test/java/com/jnape/palatable/lambda/monoid/builtin/ConcatTest.java create mode 100644 src/test/java/com/jnape/palatable/lambda/monoid/builtin/LeftAllTest.java create mode 100644 src/test/java/com/jnape/palatable/lambda/monoid/builtin/LeftAnyTest.java create mode 100644 src/test/java/com/jnape/palatable/lambda/monoid/builtin/MergeTest.java create mode 100644 src/test/java/com/jnape/palatable/lambda/monoid/builtin/PresentTest.java create mode 100644 src/test/java/com/jnape/palatable/lambda/monoid/builtin/PutAllTest.java create mode 100644 src/test/java/com/jnape/palatable/lambda/monoid/builtin/RightAllTest.java create mode 100644 src/test/java/com/jnape/palatable/lambda/monoid/builtin/RightAnyTest.java delete mode 100644 src/test/java/com/jnape/palatable/lambda/semigroup/LeftTest.java delete mode 100644 src/test/java/com/jnape/palatable/lambda/semigroup/PresentTest.java delete mode 100644 src/test/java/com/jnape/palatable/lambda/semigroup/RightTest.java create mode 100644 src/test/java/com/jnape/palatable/lambda/semigroup/builtin/CollapseTest.java create mode 100644 src/test/java/com/jnape/palatable/lambda/semigroup/builtin/ConcatTest.java create mode 100644 src/test/java/com/jnape/palatable/lambda/semigroup/builtin/LeftAllTest.java create mode 100644 src/test/java/com/jnape/palatable/lambda/semigroup/builtin/LeftAnyTest.java create mode 100644 src/test/java/com/jnape/palatable/lambda/semigroup/builtin/MergeTest.java create mode 100644 src/test/java/com/jnape/palatable/lambda/semigroup/builtin/RightAllTest.java create mode 100644 src/test/java/com/jnape/palatable/lambda/semigroup/builtin/RightAnyTest.java 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 b73f57e05..a738f7700 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 @@ -1,6 +1,7 @@ package com.jnape.palatable.lambda.adt.hlist; import com.jnape.palatable.lambda.adt.hlist.HList.HCons; +import com.jnape.palatable.lambda.monoid.Monoid; import com.jnape.palatable.lambda.functor.Bifunctor; import com.jnape.palatable.lambda.functor.Functor; @@ -90,6 +91,13 @@ public <_1Prime, _2Prime> Tuple2<_1Prime, _2Prime> biMap(Function(lFn.apply(_1()), tail().fmap(rFn)); } + public static <_1, _2> Monoid> monoid(Monoid<_1> _1Monoid, Monoid<_2> _2Monoid) { + return Monoid.monoid( + (x, y) -> x.biMap(_1Monoid.flip().apply(y._1()), + _2Monoid.flip().apply(y._2())), + tuple(_1Monoid.identity(), _2Monoid.identity())); + } + /** * Static factory method for creating Tuple2s from {@link java.util.Map.Entry}s. * @@ -101,4 +109,5 @@ public <_1Prime, _2Prime> Tuple2<_1Prime, _2Prime> biMap(Function Tuple2 fromEntry(Map.Entry entry) { return new Tuple2<>(entry.getKey(), singletonHList(entry.getValue())); } + } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn2.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn2.java index f3286a822..c449fb2aa 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/Fn2.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn2.java @@ -13,7 +13,6 @@ * @param The second argument type * @param The return type * @see Fn1 - * @see com.jnape.palatable.lambda.functions.builtin.fn2.Partial2 */ @FunctionalInterface public interface Fn2 extends Fn1> { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn3.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn3.java index 6efb9bb1b..7e21f9d79 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/Fn3.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn3.java @@ -10,7 +10,6 @@ * @param The third argument type * @param The return type * @see Fn2 - * @see com.jnape.palatable.lambda.functions.builtin.fn2.Partial3 */ @FunctionalInterface public interface Fn3 extends Fn2> { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn4.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn4.java new file mode 100644 index 000000000..cb093a7f1 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn4.java @@ -0,0 +1,85 @@ +package com.jnape.palatable.lambda.functions; + +import com.jnape.palatable.lambda.adt.hlist.Tuple2; + +/** + * A function taking four arguments. Defined in terms of Fn3, so similarly auto-curried. + * + * @param The first argument type + * @param The second argument type + * @param The third argument type + * @param The fourth argument type + * @param The return type + * @see Fn3 + */ +@FunctionalInterface +public interface Fn4 extends Fn3> { + + /** + * Invoke this function with the given arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @param d the fourth argument + * @return the result of the function application + */ + E apply(A a, B b, C c, D d); + + /** + * Partially apply this function by taking its first argument. + * + * @param a the first argument + * @return an Fn3 that takes the second, third, and fourth argument and returns the result + */ + @Override + default Fn3 apply(A a) { + return (b, c, d) -> apply(a, b, c, d); + } + + /** + * Partially apply this function by taking its first two arguments. + * + * @param a the first argument + * @param b the second argument + * @return an Fn2 that takes the third and fourth arguments and returns the result + */ + @Override + default Fn2 apply(A a, B b) { + return (c, d) -> apply(a, b, c, d); + } + + /** + * Partially apply this function by taking its first three arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @return an Fn1 that takes the fourth argument and returns the result + */ + @Override + default Fn1 apply(A a, B b, C c) { + return (d) -> apply(a, b, c, d); + } + + /** + * Flip the order of the first two arguments. + * + * @return an Fn3 that takes the first and second arguments in reversed order + */ + @Override + default Fn4 flip() { + return (b, a, c, d) -> apply(a, b, c, d); + } + + /** + * Returns an Fn3 that takes the first two arguments as a Tuple2<A, B> and the third + * and fourth arguments. + * + * @return an Fn3 taking a Tuple2 and the third and fourth arguments + */ + @Override + default Fn3, C, D, E> uncurry() { + return (ab, c, d) -> apply(ab._1(), ab._2(), c, d); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/specialized/BiMonoidFactory.java b/src/main/java/com/jnape/palatable/lambda/functions/specialized/BiMonoidFactory.java new file mode 100644 index 000000000..fee6e7339 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/specialized/BiMonoidFactory.java @@ -0,0 +1,26 @@ +package com.jnape.palatable.lambda.functions.specialized; + +import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import com.jnape.palatable.lambda.monoid.Monoid; + +@FunctionalInterface +public interface BiMonoidFactory extends BiSemigroupFactory { + + @Override + default MonoidFactory apply(A a) { + return b -> apply(a, b); + } + + @Override + Monoid apply(A a, B b); + + @Override + default BiMonoidFactory flip() { + return (b, a) -> apply(a, b); + } + + @Override + default MonoidFactory, C> uncurry() { + return ab -> apply(ab._1()).apply(ab._2()); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/specialized/BiSemigroupFactory.java b/src/main/java/com/jnape/palatable/lambda/functions/specialized/BiSemigroupFactory.java new file mode 100644 index 000000000..d7266132c --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/specialized/BiSemigroupFactory.java @@ -0,0 +1,32 @@ +package com.jnape.palatable.lambda.functions.specialized; + +import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import com.jnape.palatable.lambda.functions.Fn4; +import com.jnape.palatable.lambda.semigroup.Semigroup; + +@FunctionalInterface +public interface BiSemigroupFactory extends Fn4 { + + @Override + Semigroup apply(A a, B b); + + @Override + default SemigroupFactory apply(A a) { + return b -> apply(a, b); + } + + @Override + default BiSemigroupFactory flip() { + return (b, a) -> apply(a, b); + } + + @Override + default SemigroupFactory, C> uncurry() { + return ab -> apply(ab._1(), ab._2()); + } + + @Override + default C apply(A a, B b, C c, C d) { + return apply(a).apply(b).apply(c, d); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/specialized/MonoidFactory.java b/src/main/java/com/jnape/palatable/lambda/functions/specialized/MonoidFactory.java new file mode 100644 index 000000000..68657a104 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/specialized/MonoidFactory.java @@ -0,0 +1,14 @@ +package com.jnape.palatable.lambda.functions.specialized; + +import com.jnape.palatable.lambda.monoid.Monoid; + +public interface MonoidFactory extends SemigroupFactory { + + @Override + default B apply(A a, B b, B c) { + return apply(a).apply(b, c); + } + + @Override + Monoid apply(A a); +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/specialized/SemigroupFactory.java b/src/main/java/com/jnape/palatable/lambda/functions/specialized/SemigroupFactory.java new file mode 100644 index 000000000..4a47c277c --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/specialized/SemigroupFactory.java @@ -0,0 +1,16 @@ +package com.jnape.palatable.lambda.functions.specialized; + +import com.jnape.palatable.lambda.functions.Fn3; +import com.jnape.palatable.lambda.semigroup.Semigroup; + +@FunctionalInterface +public interface SemigroupFactory extends Fn3 { + + @Override + Semigroup apply(A a); + + @Override + default B apply(A a, B b, B c) { + return apply(a).apply(b, c); + } +} 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 d392414da..0f7df445b 100644 --- a/src/main/java/com/jnape/palatable/lambda/monoid/Monoid.java +++ b/src/main/java/com/jnape/palatable/lambda/monoid/Monoid.java @@ -6,6 +6,7 @@ import com.jnape.palatable.lambda.semigroup.Semigroup; import java.util.function.Function; +import java.util.function.Supplier; import static com.jnape.palatable.lambda.functions.builtin.fn2.Map.map; @@ -84,4 +85,18 @@ public A apply(A x, A y) { } }; } + + static Monoid monoid(Semigroup semigroup, Supplier identitySupplier) { + return new Monoid() { + @Override + public A identity() { + return identitySupplier.get(); + } + + @Override + public A apply(A x, A y) { + return semigroup.apply(x, y); + } + }; + } } diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Collapse.java b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Collapse.java new file mode 100644 index 000000000..51cf36970 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Collapse.java @@ -0,0 +1,59 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.specialized.BiMonoidFactory; +import com.jnape.palatable.lambda.functions.specialized.MonoidFactory; +import com.jnape.palatable.lambda.monoid.Monoid; +import com.jnape.palatable.lambda.semigroup.Semigroup; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; + +/** + * A {@link Monoid} instance formed by a {@link Tuple2}<_1, _2> and monoids over _1 and + * _2. Successively collapses multiple {@link Tuple2}s into a single {@link Tuple2} by collapsing the + * values of each slot under the provided monoid instance. + *

+ * For the {@link Semigroup}, see {@link com.jnape.palatable.lambda.semigroup.builtin.Collapse}. + * + * @param <_1> the first slot parameter type + * @param <_2> the second slot parameter type + * @see Monoid + * @see Tuple2 + */ +public final class Collapse<_1, _2> implements BiMonoidFactory, Monoid<_2>, Tuple2<_1, _2>> { + + private static final Collapse INSTANCE = new Collapse(); + + private Collapse() { + } + + @Override + public Monoid> apply(Monoid<_1> _1Monoid, Monoid<_2> _2Monoid) { + Semigroup> semigroup = com.jnape.palatable.lambda.semigroup.builtin.Collapse.collapse(_1Monoid, _2Monoid); + return Monoid.>monoid(semigroup, () -> tuple(_1Monoid.identity(), _2Monoid.identity())); + } + + @SuppressWarnings("unchecked") + public static <_1, _2> Collapse<_1, _2> collapse() { + return INSTANCE; + } + + public static <_1, _2> MonoidFactory, Tuple2<_1, _2>> collapse(Monoid<_1> _1Monoid) { + return Collapse.<_1, _2>collapse().apply(_1Monoid); + } + + public static <_1, _2> Monoid> collapse(Monoid<_1> _1Monoid, Monoid<_2> _2Monoid) { + return Collapse.<_1, _2>collapse(_1Monoid).apply(_2Monoid); + } + + public static <_1, _2> Fn1, Tuple2<_1, _2>> collapse(Monoid<_1> _1Monoid, Monoid<_2> _2Monoid, + Tuple2<_1, _2> x) { + return collapse(_1Monoid, _2Monoid).apply(x); + } + + public static <_1, _2> Tuple2<_1, _2> collapse(Monoid<_1> _1Monoid, Monoid<_2> _2Monoid, Tuple2<_1, _2> x, + Tuple2<_1, _2> y) { + return collapse(_1Monoid, _2Monoid, x).apply(y); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Concat.java b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Concat.java new file mode 100644 index 000000000..2ea4501fe --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Concat.java @@ -0,0 +1,50 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.specialized.MonoidFactory; +import com.jnape.palatable.lambda.monoid.Monoid; +import com.jnape.palatable.lambda.semigroup.Semigroup; + +import java.util.Collection; +import java.util.function.Supplier; + +import static com.jnape.palatable.lambda.monoid.Monoid.monoid; + +/** + * The {@link Monoid} instance formed under concatenation for an arbitrary {@link Collection}. The collection subtype + * (C) must support {@link Collection#addAll(Collection)}. + *

+ * For the {@link Semigroup}, see {@link com.jnape.palatable.lambda.semigroup.builtin.Concat}. + * + * @see Monoid + */ +public final class Concat> implements MonoidFactory, C> { + + private static final Concat INSTANCE = new Concat(); + + private Concat() { + } + + @Override + public Monoid apply(Supplier cSupplier) { + Semigroup semigroup = com.jnape.palatable.lambda.semigroup.builtin.Concat.concat(); + return monoid(semigroup, cSupplier); + } + + @SuppressWarnings("unchecked") + public static > Concat concat() { + return INSTANCE; + } + + public static > Monoid concat(Supplier collectionSupplier) { + return Concat.concat().apply(collectionSupplier); + } + + public static > Fn1 concat(Supplier collectionSupplier, C xs) { + return concat(collectionSupplier).apply(xs); + } + + public static > C concat(Supplier collectionSupplier, C xs, C ys) { + return concat(collectionSupplier, xs).apply(ys); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/LeftAll.java b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/LeftAll.java new file mode 100644 index 000000000..d5fa005a6 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/LeftAll.java @@ -0,0 +1,59 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.adt.Either; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.specialized.MonoidFactory; +import com.jnape.palatable.lambda.monoid.Monoid; +import com.jnape.palatable.lambda.semigroup.Semigroup; + +import static com.jnape.palatable.lambda.adt.Either.left; + +/** + * A {@link Monoid} instance formed by {@link Either}<L,R> and a monoid over L. The + * application to two {@link Either} values short-circuits on right values, such that for a given {@link Either} + * x and y: + *

    + *
  • if x is a Right value, the result is x
  • + *
  • if y is a Right value, the result is y
  • + *
  • if both x and y are left values, the result is the application of the x and y values + * in terms of the provided monoid, wrapped in {@link Either#left}
  • + *
+ *

+ * For the {@link Semigroup}, see {@link com.jnape.palatable.lambda.semigroup.builtin.LeftAll}. + * + * @param The left parameter type + * @param The right parameter type + * @see Monoid + * @see RightAll + * @see Either + */ +public class LeftAll implements MonoidFactory, Either> { + + private static final LeftAll INSTANCE = new LeftAll(); + + private LeftAll() { + } + + @Override + public Monoid> apply(Monoid lMonoid) { + Semigroup> semigroup = com.jnape.palatable.lambda.semigroup.builtin.LeftAll.leftAll(lMonoid); + return Monoid.>monoid(semigroup, () -> left(lMonoid.identity())); + } + + @SuppressWarnings("unchecked") + public static LeftAll leftAll() { + return INSTANCE; + } + + public static Monoid> leftAll(Monoid lMonoid) { + return LeftAll.leftAll().apply(lMonoid); + } + + public static Fn1, Either> leftAll(Monoid lMonoid, Either x) { + return LeftAll.leftAll(lMonoid).apply(x); + } + + public static Either leftAll(Monoid lMonoid, Either x, Either y) { + return leftAll(lMonoid, x).apply(y); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/LeftAny.java b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/LeftAny.java new file mode 100644 index 000000000..a9035d67c --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/LeftAny.java @@ -0,0 +1,59 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.adt.Either; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.specialized.MonoidFactory; +import com.jnape.palatable.lambda.monoid.Monoid; +import com.jnape.palatable.lambda.semigroup.Semigroup; + +import static com.jnape.palatable.lambda.adt.Either.left; + +/** + * A {@link Monoid} instance formed by {@link Either}<L,R> and a monoid over L. The + * application to two {@link Either} values is left-biased, such that for a given {@link Either} x and + * y: + *

    + *
  • if both x and y are left values, the result is the application of the x and y values + * in terms of the provided monoid, wrapped in {@link Either#left}
  • + *
  • if only x is a left value, the result is x
  • + *
  • if only y is a left value, the result is y
  • + *
  • if neither x nor y are left values, the result is y
  • + *
+ *

+ * For the {@link Semigroup}, see {@link com.jnape.palatable.lambda.semigroup.builtin.LeftAny}. + * + * @param The left parameter type + * @param The right parameter type + * @see Monoid + * @see Either + */ +public final class LeftAny implements MonoidFactory, Either> { + + private static final LeftAny INSTANCE = new LeftAny(); + + private LeftAny() { + } + + @Override + public Monoid> apply(Monoid lMonoid) { + Semigroup> semigroup = com.jnape.palatable.lambda.semigroup.builtin.LeftAny.leftAny(lMonoid); + return Monoid.>monoid(semigroup, () -> left(lMonoid.identity())); + } + + @SuppressWarnings("unchecked") + public static LeftAny leftAny() { + return INSTANCE; + } + + public static Monoid> leftAny(Monoid lMonoid) { + return LeftAny.leftAny().apply(lMonoid); + } + + public static Fn1, Either> leftAny(Monoid lMonoid, Either x) { + return LeftAny.leftAny(lMonoid).apply(x); + } + + public static Either leftAny(Monoid lMonoid, Either x, Either y) { + return leftAny(lMonoid, x).apply(y); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Merge.java b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Merge.java new file mode 100644 index 000000000..798b6041a --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Merge.java @@ -0,0 +1,58 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.adt.Either; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.specialized.BiMonoidFactory; +import com.jnape.palatable.lambda.functions.specialized.MonoidFactory; +import com.jnape.palatable.lambda.monoid.Monoid; +import com.jnape.palatable.lambda.semigroup.Semigroup; + +import static com.jnape.palatable.lambda.adt.Either.right; + +/** + * A {@link Monoid} instance formed by {@link Either#merge}, a semigroup over L, and a monoid over + * R. Like {@link Either#merge}, this is left-biased. + *

+ * For the {@link Semigroup}, see {@link com.jnape.palatable.lambda.semigroup.builtin.Merge}. + * + * @param The left parameter type + * @param The right parameter type + * @see Monoid + * @see Either#merge + */ +public final class Merge implements BiMonoidFactory, Monoid, Either> { + + private static final Merge INSTANCE = new Merge(); + + private Merge() { + } + + @Override + public Monoid> apply(Semigroup lSemigroup, Monoid rMonoid) { + Semigroup> semigroup = com.jnape.palatable.lambda.semigroup.builtin.Merge.merge(lSemigroup, rMonoid); + return Monoid.>monoid(semigroup, () -> right(rMonoid.identity())); + } + + @SuppressWarnings("unchecked") + public static Merge merge() { + return INSTANCE; + } + + public static MonoidFactory, Either> merge(Semigroup lSemigroup) { + return Merge.merge().apply(lSemigroup); + } + + public static Monoid> merge(Semigroup lSemigroup, Monoid rMonoid) { + return Merge.merge(lSemigroup).apply(rMonoid); + } + + public static Fn1, Either> merge(Semigroup lSemigroup, Monoid rMonoid, + Either x) { + return merge(lSemigroup, rMonoid).apply(x); + } + + public static Either merge(Semigroup lSemigroup, Monoid rMonoid, Either x, + Either y) { + return merge(lSemigroup, rMonoid, x).apply(y); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Present.java b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Present.java new file mode 100644 index 000000000..371ede8b3 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Present.java @@ -0,0 +1,58 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.specialized.MonoidFactory; +import com.jnape.palatable.lambda.monoid.Monoid; +import com.jnape.palatable.lambda.semigroup.Semigroup; + +import java.util.Optional; +import java.util.function.Function; + +import static com.jnape.palatable.lambda.monoid.Monoid.monoid; + +/** + * A {@link Monoid} instance formed by {@link Optional}<A> and a semigroup over A. The + * application to two {@link Optional} values is present-biased, such that for a given {@link Optional} x + * and y: + *

    + *
  • if x is a present value and y is empty, the result is x
  • + *
  • if x is an empty value and y is present, the result is y
  • + *
  • if both x and y are present, the result is the application of the x and y values in + * terms of the provided semigroup, wrapped in a present Optional
  • + *
  • if both x and y are empty, the result is empty
  • + *
+ * + * @param
the Optional value parameter type + * @see Monoid + * @see Optional + */ +public final class Present implements MonoidFactory, Optional> { + + private Present() { + } + + @Override + public Monoid> apply(Semigroup aSemigroup) { + Semigroup> semigroup = (optX, optY) -> { + Function> combine = x -> Optional.of(optY.map(aSemigroup.apply(x)).orElse(x)); + return optX.map(combine).orElse(optY); + }; + return monoid(semigroup, Optional.empty()); + } + + public static Present present() { + return new Present<>(); + } + + public static Monoid> present(Semigroup semigroup) { + return Present.present().apply(semigroup); + } + + public static Fn1, Optional> present(Semigroup aSemigroup, Optional x) { + return present(aSemigroup).apply(x); + } + + public static Optional present(Semigroup semigroup, Optional x, Optional y) { + return present(semigroup, x).apply(y); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/PutAll.java b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/PutAll.java new file mode 100644 index 000000000..b5c4e7ef8 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/PutAll.java @@ -0,0 +1,43 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.adt.hmap.HMap; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.monoid.Monoid; + +import static com.jnape.palatable.lambda.adt.hmap.HMap.emptyHMap; + +/** + * A {@link Monoid} instance formed by {@link HMap} that simply combines all the mappings. + * + * @see Monoid + * @see HMap + */ +public final class PutAll implements Monoid { + + private static final PutAll INSTANCE = new PutAll(); + + private PutAll() { + } + + @Override + public HMap identity() { + return emptyHMap(); + } + + @Override + public HMap apply(HMap x, HMap y) { + return x.putAll(y); + } + + public static PutAll putAll() { + return INSTANCE; + } + + public static Fn1 putAll(HMap x) { + return putAll().apply(x); + } + + public static HMap putAll(HMap x, HMap y) { + return putAll(x).apply(y); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/RightAll.java b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/RightAll.java new file mode 100644 index 000000000..f68fb82e2 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/RightAll.java @@ -0,0 +1,56 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.adt.Either; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.specialized.MonoidFactory; +import com.jnape.palatable.lambda.monoid.Monoid; +import com.jnape.palatable.lambda.semigroup.Semigroup; + +import static com.jnape.palatable.lambda.adt.Either.right; + +/** + * A {@link Monoid} instance formed by {@link Either}<L,R> and a monoid over R. The + * application to two {@link Either} values is left-biased, such that for a given {@link Either} x and + * y: + *
    + *
  • if x is a Left value, the result is x
  • + *
  • if y is a Left value, the result is y
  • + *
  • if both x and y are right values, the result is the application of the x and y values + * in terms of the provided monoid, wrapped in {@link Either#right}
  • + *
+ *

+ * For the {@link Semigroup}, see {@link com.jnape.palatable.lambda.semigroup.builtin.RightAll}. + * + * @param The left parameter type + * @param The right parameter type + * @see Monoid + * @see LeftAll + * @see Either + */ +public final class RightAll implements MonoidFactory, Either> { + + private RightAll() { + } + + @Override + public Monoid> apply(Monoid rMonoid) { + Semigroup> semigroup = com.jnape.palatable.lambda.semigroup.builtin.RightAll.rightAll(rMonoid); + return Monoid.>monoid(semigroup, () -> right(rMonoid.identity())); + } + + public static RightAll rightAll() { + return new RightAll<>(); + } + + public static Monoid> rightAll(Monoid rMonoid) { + return RightAll.rightAll().apply(rMonoid); + } + + public static Fn1, Either> rightAll(Monoid rMonoid, Either x) { + return RightAll.rightAll(rMonoid).apply(x); + } + + public static Either rightAll(Monoid rMonoid, Either x, Either y) { + return rightAll(rMonoid, x).apply(y); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/RightAny.java b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/RightAny.java new file mode 100644 index 000000000..ad812cffd --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/RightAny.java @@ -0,0 +1,59 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.adt.Either; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.specialized.MonoidFactory; +import com.jnape.palatable.lambda.monoid.Monoid; +import com.jnape.palatable.lambda.semigroup.Semigroup; + +import static com.jnape.palatable.lambda.adt.Either.right; + +/** + * A {@link Monoid} instance formed by {@link Either}<L,R> and a monoid over R. The + * application to two {@link Either} values is right-biased, such that for a given {@link Either} x and + * y: + *

    + *
  • if both x and y are right values, the result is the application of the x and y values + * in terms of the provided monoid, wrapped in {@link Either#right}
  • + *
  • if only x is a right value, the result is x
  • + *
  • if only y is a right value, the result is y
  • + *
  • if neither x nor y are right values, the result is y
  • + *
+ *

+ * For the {@link Semigroup}, see {@link com.jnape.palatable.lambda.semigroup.builtin.RightAny}. + * + * @param The left parameter type + * @param The right parameter type + * @see Monoid + * @see Either + */ +public final class RightAny implements MonoidFactory, Either> { + + private static final RightAny INSTANCE = new RightAny(); + + private RightAny() { + } + + @Override + public Monoid> apply(Monoid rMonoid) { + Semigroup> semigroup = com.jnape.palatable.lambda.semigroup.builtin.RightAny.rightAny(rMonoid); + return Monoid.>monoid(semigroup, () -> right(rMonoid.identity())); + } + + @SuppressWarnings("unchecked") + public static RightAny rightAny() { + return INSTANCE; + } + + public static Semigroup> rightAny(Monoid rMonoid) { + return RightAny.rightAny().apply(rMonoid); + } + + public static Fn1, Either> rightAny(Monoid rMonoid, Either x) { + return RightAny.rightAny(rMonoid).apply(x); + } + + public static Either rightAny(Monoid rMonoid, Either x, Either y) { + return rightAny(rMonoid, x).apply(y); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/semigroup/Left.java b/src/main/java/com/jnape/palatable/lambda/semigroup/Left.java deleted file mode 100644 index 329bbc723..000000000 --- a/src/main/java/com/jnape/palatable/lambda/semigroup/Left.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.jnape.palatable.lambda.semigroup; - -import com.jnape.palatable.lambda.adt.Either; - -import static com.jnape.palatable.lambda.adt.Either.left; - -/** - * The {@link Semigroup} formed by {@link Either}<L,R> and a Semigroup over - * L. The application to two {@link Either} values is right-biased, such that for a given {@link - * Either} x and y: - *

    - *
  • if x is a Right value, the result is x
  • - *
  • if y is a Right value, the result is y
  • - *
  • if both x and y are Left values, the result is the application of the x - * and y values in terms of the provided semigroup, wrapped in a Left
  • - *
- * - * @param the left parameter type - * @param the right parameter type - */ -public final class Left implements Semigroup> { - - private final Semigroup semigroup; - - public Left(Semigroup semigroup) { - this.semigroup = semigroup; - } - - @Override - public Either apply(Either x, Either y) { - return x.flatMap(xL -> y.flatMap(yL -> left(semigroup.apply(xL, yL)), - Either::right), - Either::right); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/semigroup/Present.java b/src/main/java/com/jnape/palatable/lambda/semigroup/Present.java deleted file mode 100644 index 5f234ca42..000000000 --- a/src/main/java/com/jnape/palatable/lambda/semigroup/Present.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.jnape.palatable.lambda.semigroup; - -import com.jnape.palatable.lambda.monoid.Monoid; - -import java.util.Optional; - -/** - * The {@link Semigroup} formed by {@link Optional}<A> and a Semigroup over - * A. The application to two {@link Optional} values is empty-biased, such that for a given {@link - * Optional} x and y: - *
    - *
  • if x is Optional.empty() value, the result is x
  • - *
  • if y is Optional.empty() value, the result is y
  • - *
  • if both x and y are present, the result is the application of the x and y values in - * terms of the provided semigroup, wrapped in a present Optional
  • - *
- * - * @param
the Optional value parameter type - */ -public final class Present implements Monoid> { - - private Semigroup semigroup; - - public Present(Semigroup semigroup) { - this.semigroup = semigroup; - } - - @Override - public Optional identity() { - return Optional.empty(); - } - - @Override - public Optional apply(Optional optX, Optional optY) { - return optX.flatMap(x -> optY.flatMap(y -> Optional.of(semigroup.apply(x, y)))); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/semigroup/Right.java b/src/main/java/com/jnape/palatable/lambda/semigroup/Right.java deleted file mode 100644 index cdeeff324..000000000 --- a/src/main/java/com/jnape/palatable/lambda/semigroup/Right.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.jnape.palatable.lambda.semigroup; - -import com.jnape.palatable.lambda.adt.Either; - -import static com.jnape.palatable.lambda.adt.Either.right; - -/** - * The {@link Semigroup} formed by {@link Either}<L,R> and a Semigroup over - * R. The application to two {@link Either} values is left-biased, such that for a given {@link - * Either} x and y: - *
    - *
  • if x is a Left value, the result is x
  • - *
  • if y is a Left value, the result is y
  • - *
  • if both x and y are right values, the result is the application of the x - * and y values in terms of the provided semigroup, wrapped in a Right
  • - *
- * - * @param the left parameter type - * @param the right parameter type - */ -public final class Right implements Semigroup> { - - private final Semigroup semigroup; - - public Right(Semigroup semigroup) { - this.semigroup = semigroup; - } - - @Override - public Either apply(Either eitherX, Either eitherY) { - return eitherX.flatMap(xR -> eitherY.flatMap(yR -> right(semigroup.apply(xR, yR)))); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Collapse.java b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Collapse.java new file mode 100644 index 000000000..9c2046632 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Collapse.java @@ -0,0 +1,59 @@ +package com.jnape.palatable.lambda.semigroup.builtin; + +import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.specialized.BiSemigroupFactory; +import com.jnape.palatable.lambda.functions.specialized.SemigroupFactory; +import com.jnape.palatable.lambda.monoid.Monoid; +import com.jnape.palatable.lambda.semigroup.Semigroup; + +/** + * A {@link Semigroup} instance formed by a {@link Tuple2}<_1, _2> and semigroups over + * _1 and _2. Successively collapses multiple {@link Tuple2}s into a single {@link Tuple2} by + * collapsing the values of each slot under the provided semigroup instance. + *

+ * For the {@link Monoid}, see {@link com.jnape.palatable.lambda.monoid.builtin.Collapse}. + * + * @param <_1> the first slot parameter type + * @param <_2> the second slot parameter type + * @see Semigroup + * @see Tuple2 + */ +public final class Collapse<_1, _2> implements BiSemigroupFactory, Semigroup<_2>, Tuple2<_1, _2>> { + + private static final Collapse INSTANCE = new Collapse(); + + private Collapse() { + } + + @Override + public Semigroup> apply(Semigroup<_1> _1Semigroup, Semigroup<_2> _2Semigroup) { + return (x, y) -> x.biMap(_1Semigroup.flip().apply(y._1()), + _2Semigroup.flip().apply(y._2())); + } + + @SuppressWarnings("unchecked") + public static <_1, _2> Collapse<_1, _2> collapse() { + return INSTANCE; + } + + public static <_1, _2> SemigroupFactory, Tuple2<_1, _2>> collapse(Semigroup<_1> _1Semigroup) { + return Collapse.<_1, _2>collapse().apply(_1Semigroup); + } + + public static <_1, _2> Semigroup> collapse(Semigroup<_1> _1Semigroup, Semigroup<_2> _2Semigroup) { + return Collapse.<_1, _2>collapse(_1Semigroup).apply(_2Semigroup); + } + + public static <_1, _2> Fn1, Tuple2<_1, _2>> collapse(Semigroup<_1> _1Semigroup, + Semigroup<_2> _2Semigroup, + Tuple2<_1, _2> x) { + return collapse(_1Semigroup, _2Semigroup).apply(x); + } + + public static <_1, _2> Tuple2<_1, _2> collapse(Semigroup<_1> _1Semigroup, Semigroup<_2> _2Semigroup, + Tuple2<_1, _2> x, + Tuple2<_1, _2> y) { + return collapse(_1Semigroup, _2Semigroup, x).apply(y); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Concat.java b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Concat.java new file mode 100644 index 000000000..9d119a97f --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Concat.java @@ -0,0 +1,42 @@ +package com.jnape.palatable.lambda.semigroup.builtin; + +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.monoid.Monoid; +import com.jnape.palatable.lambda.semigroup.Semigroup; + +import java.util.Collection; + +/** + * The {@link Semigroup} instance formed under concatenation for an arbitrary {@link Collection}. The collection subtype + * (C) must support {@link Collection#addAll(Collection)}. + *

+ * For the {@link Monoid}, see {@link com.jnape.palatable.lambda.monoid.builtin.Concat}. + * + * @see Semigroup + */ +public final class Concat> implements Semigroup { + + private static final Concat INSTANCE = new Concat(); + + private Concat() { + } + + @Override + public C apply(C xs, C ys) { + xs.addAll(ys); + return xs; + } + + @SuppressWarnings("unchecked") + public static > Concat concat() { + return INSTANCE; + } + + public static > Fn1 concat(C xs) { + return Concat.concat().apply(xs); + } + + public static > C concat(C xs, C ys) { + return concat(xs).apply(ys); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/LeftAll.java b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/LeftAll.java new file mode 100644 index 000000000..70ef187fd --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/LeftAll.java @@ -0,0 +1,59 @@ +package com.jnape.palatable.lambda.semigroup.builtin; + +import com.jnape.palatable.lambda.adt.Either; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.specialized.SemigroupFactory; +import com.jnape.palatable.lambda.monoid.Monoid; +import com.jnape.palatable.lambda.monoid.builtin.RightAll; +import com.jnape.palatable.lambda.semigroup.Semigroup; + +import static com.jnape.palatable.lambda.adt.Either.left; + +/** + * A {@link Semigroup} instance formed by {@link Either}<L,R> and a semigroup over L. + * The application to two {@link Either} values is right-biased, such that for a given {@link Either} x and + * y: + *

    + *
  • if x is a Right value, the result is x
  • + *
  • if y is a Right value, the result is y
  • + *
  • if both x and y are left values, the result is the application of the x and y values + * in terms of the provided semigroup, wrapped in {@link Either#left}
  • + *
+ *

+ * For the {@link Monoid}, see {@link com.jnape.palatable.lambda.monoid.builtin.LeftAll}. + * + * @param The left parameter type + * @param The right parameter type + * @see Semigroup + * @see RightAll + * @see Either + */ +public final class LeftAll implements SemigroupFactory, Either> { + + private static final LeftAll INSTANCE = new LeftAll(); + + private LeftAll() { + } + + @Override + public Semigroup> apply(Semigroup lSemigroup) { + return (x, y) -> x.flatMap(xL -> y.flatMap(yL -> left(lSemigroup.apply(xL, yL)), Either::right), Either::right); + } + + @SuppressWarnings("unchecked") + public static LeftAll leftAll() { + return INSTANCE; + } + + public static Semigroup> leftAll(Semigroup lSemigroup) { + return LeftAll.leftAll().apply(lSemigroup); + } + + public static Fn1, Either> leftAll(Semigroup lSemigroup, Either x) { + return LeftAll.leftAll(lSemigroup).apply(x); + } + + public static Either leftAll(Semigroup lSemigroup, Either x, Either y) { + return leftAll(lSemigroup, x).apply(y); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/LeftAny.java b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/LeftAny.java new file mode 100644 index 000000000..1bc166dca --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/LeftAny.java @@ -0,0 +1,60 @@ +package com.jnape.palatable.lambda.semigroup.builtin; + +import com.jnape.palatable.lambda.adt.Either; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.specialized.SemigroupFactory; +import com.jnape.palatable.lambda.monoid.Monoid; +import com.jnape.palatable.lambda.semigroup.Semigroup; + +import static com.jnape.palatable.lambda.adt.Either.left; + +/** + * A {@link Semigroup} instance formed by {@link Either}<L,R> and a semigroup over L. + * The application to two {@link Either} values is left-biased, such that for a given {@link Either} x and + * y: + *

    + *
  • if both x and y are left values, the result is the application of the x and y values + * in terms of the provided semigroup, wrapped in {@link Either#left}
  • + *
  • if only x is a left value, the result is x
  • + *
  • if only y is a left value, the result is y
  • + *
  • if neither x nor y are left values, the result is y
  • + *
+ *

+ * For the {@link Monoid}, see {@link com.jnape.palatable.lambda.monoid.builtin.LeftAny}. + * + * @param The left parameter type + * @param The right parameter type + * @see Semigroup + * @see Either + */ +public final class LeftAny implements SemigroupFactory, Either> { + + private static final LeftAny INSTANCE = new LeftAny(); + + private LeftAny() { + } + + @Override + public Semigroup> apply(Semigroup lSemigroup) { + return (x, y) -> x.flatMap(xL -> y.flatMap(yL -> left(lSemigroup.apply(xL, yL)), + yR -> left(xL)), + xR -> y); + } + + @SuppressWarnings("unchecked") + public static LeftAny leftAny() { + return INSTANCE; + } + + public static Semigroup> leftAny(Semigroup lSemigroup) { + return LeftAny.leftAny().apply(lSemigroup); + } + + public static Fn1, Either> leftAny(Semigroup lSemigroup, Either x) { + return LeftAny.leftAny(lSemigroup).apply(x); + } + + public static Either leftAny(Semigroup lSemigroup, Either x, Either y) { + return leftAny(lSemigroup, x).apply(y); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Merge.java b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Merge.java new file mode 100644 index 000000000..75bd81ffd --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/Merge.java @@ -0,0 +1,55 @@ +package com.jnape.palatable.lambda.semigroup.builtin; + +import com.jnape.palatable.lambda.adt.Either; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.specialized.BiSemigroupFactory; +import com.jnape.palatable.lambda.functions.specialized.SemigroupFactory; +import com.jnape.palatable.lambda.monoid.Monoid; +import com.jnape.palatable.lambda.semigroup.Semigroup; + +/** + * A {@link Semigroup} instance formed by {@link Either#merge} and semigroups over L and R. + * Like {@link Either#merge}, this is left-biased. + *

+ * For the {@link Monoid}, see {@link com.jnape.palatable.lambda.monoid.builtin.Merge}. + * + * @param The left parameter type + * @param The right parameter type + * @see Monoid + * @see Either#merge + */ +public final class Merge implements BiSemigroupFactory, Semigroup, Either> { + + private static final Merge INSTANCE = new Merge(); + + private Merge() { + } + + @Override + public Semigroup> apply(Semigroup lSemigroup, Semigroup rSemigroup) { + return (x, y) -> x.merge(lSemigroup::apply, rSemigroup::apply, y); + } + + @SuppressWarnings("unchecked") + public static Merge merge() { + return INSTANCE; + } + + public static SemigroupFactory, Either> merge(Semigroup lSemigroup) { + return Merge.merge().apply(lSemigroup); + } + + public static Semigroup> merge(Semigroup lSemigroup, Semigroup rSemigroup) { + return Merge.merge(lSemigroup).apply(rSemigroup); + } + + public static Fn1, Either> merge(Semigroup lSemigroup, Semigroup rSemigroup, + Either x) { + return merge(lSemigroup, rSemigroup).apply(x); + } + + public static Either merge(Semigroup lSemigroup, Semigroup rSemigroup, Either x, + Either y) { + return merge(lSemigroup, rSemigroup, x).apply(y); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/RightAll.java b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/RightAll.java new file mode 100644 index 000000000..7f1bdab5b --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/RightAll.java @@ -0,0 +1,58 @@ +package com.jnape.palatable.lambda.semigroup.builtin; + +import com.jnape.palatable.lambda.adt.Either; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.specialized.SemigroupFactory; +import com.jnape.palatable.lambda.monoid.Monoid; +import com.jnape.palatable.lambda.semigroup.Semigroup; + +import static com.jnape.palatable.lambda.adt.Either.right; + +/** + * A {@link Semigroup} instance formed by {@link Either}<L,R> and a semigroup over R. + * The application to two {@link Either} values is left-biased, such that for a given {@link Either} x and + * y: + *

    + *
  • if x is a Left value, the result is x
  • + *
  • if y is a Left value, the result is y
  • + *
  • if both x and y are right values, the result is the application of the x and y values + * in terms of the provided semigroup, wrapped in {@link Either#right}
  • + *
+ *

+ * For the {@link Monoid}, see {@link com.jnape.palatable.lambda.monoid.builtin.RightAll}. + * + * @param The left parameter type + * @param The right parameter type + * @see Semigroup + * @see LeftAll + * @see Either + */ +public final class RightAll implements SemigroupFactory, Either> { + + private static final RightAll INSTANCE = new RightAll(); + + private RightAll() { + } + + @Override + public Semigroup> apply(Semigroup rSemigroup) { + return (eitherX, eitherY) -> eitherX.flatMap(xR -> eitherY.flatMap(yR -> right(rSemigroup.apply(xR, yR)))); + } + + @SuppressWarnings("unchecked") + public static RightAll rightAll() { + return INSTANCE; + } + + public static Semigroup> rightAll(Semigroup rSemigroup) { + return RightAll.rightAll().apply(rSemigroup); + } + + public static Fn1, Either> rightAll(Semigroup rSemigroup, Either x) { + return RightAll.rightAll(rSemigroup).apply(x); + } + + public static Either rightAll(Semigroup rSemigroup, Either x, Either y) { + return rightAll(rSemigroup, x).apply(y); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/RightAny.java b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/RightAny.java new file mode 100644 index 000000000..04531cbfb --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/semigroup/builtin/RightAny.java @@ -0,0 +1,61 @@ +package com.jnape.palatable.lambda.semigroup.builtin; + +import com.jnape.palatable.lambda.adt.Either; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.specialized.SemigroupFactory; +import com.jnape.palatable.lambda.monoid.Monoid; +import com.jnape.palatable.lambda.semigroup.Semigroup; + +import static com.jnape.palatable.lambda.adt.Either.right; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly; + +/** + * A {@link Semigroup} instance formed by {@link Either}<L,R> and a semigroup over R. + * The application to two {@link Either} values is right-biased, such that for a given {@link Either} x and + * y: + *

    + *
  • if both x and y are right values, the result is the application of the x and y values + * in terms of the provided semigroup, wrapped in {@link Either#right}
  • + *
  • if only x is a right value, the result is x
  • + *
  • if only y is a right value, the result is y
  • + *
  • if neither x nor y are right values, the result is y
  • + *
+ *

+ * For the {@link Monoid}, see {@link com.jnape.palatable.lambda.monoid.builtin.RightAny}. + * + * @param The left parameter type + * @param The right parameter type + * @see Semigroup + * @see Either + */ +public final class RightAny implements SemigroupFactory, Either> { + + private static final RightAny INSTANCE = new RightAny(); + + private RightAny() { + } + + @Override + public Semigroup> apply(Semigroup rSemigroup) { + return (x, y) -> x.flatMap(constantly(y), + xR -> y.flatMap(constantly(right(xR)), + rSemigroup.apply(xR).andThen(Either::right))); + } + + @SuppressWarnings("unchecked") + public static RightAny rightAny() { + return INSTANCE; + } + + public static Semigroup> rightAny(Semigroup rSemigroup) { + return RightAny.rightAny().apply(rSemigroup); + } + + public static Fn1, Either> rightAny(Semigroup rSemigroup, Either x) { + return RightAny.rightAny(rSemigroup).apply(x); + } + + public static Either rightAny(Semigroup rSemigroup, Either x, Either y) { + return rightAny(rSemigroup, x).apply(y); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/MonoidTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/MonoidTest.java index 8b14f3dfa..730228769 100644 --- a/src/test/java/com/jnape/palatable/lambda/monoid/MonoidTest.java +++ b/src/test/java/com/jnape/palatable/lambda/monoid/MonoidTest.java @@ -1,5 +1,6 @@ package com.jnape.palatable.lambda.monoid; +import com.jnape.palatable.lambda.monoid.Monoid; import org.junit.Test; import java.util.List; diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/CollapseTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/CollapseTest.java new file mode 100644 index 000000000..1e0f36e9e --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/CollapseTest.java @@ -0,0 +1,22 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.monoid.Monoid; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.monoid.builtin.Collapse.collapse; +import static org.junit.Assert.assertEquals; + +public class CollapseTest { + + @Test + public void monoid() { + Monoid join = Monoid.monoid((x, y) -> x + y, ""); + Monoid add = Monoid.monoid((x, y) -> x + y, 0); + + Collapse collapse = collapse(); + + assertEquals(tuple("", 0), collapse.apply(join, add).identity()); + assertEquals(tuple("foobar", 3), collapse.apply(join, add, tuple("foo", 1), tuple("bar", 2))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/ConcatTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/ConcatTest.java new file mode 100644 index 000000000..f48e628a2 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/ConcatTest.java @@ -0,0 +1,25 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.monoid.Monoid; +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; + +import static com.jnape.palatable.lambda.monoid.builtin.Concat.concat; +import static java.util.Collections.singleton; +import static org.junit.Assert.assertEquals; + +public class ConcatTest { + + @Test + public void monoid() { + Monoid> concat = concat(HashSet::new); + + assertEquals(new HashSet<>(), concat.identity()); + assertEquals(new HashSet() {{ + add(1); + add(2); + }}, concat.apply(new HashSet<>(singleton(1)), new HashSet<>(singleton(2)))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/LeftAllTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/LeftAllTest.java new file mode 100644 index 000000000..2bded0487 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/LeftAllTest.java @@ -0,0 +1,24 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.monoid.Monoid; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.Either.left; +import static com.jnape.palatable.lambda.adt.Either.right; +import static com.jnape.palatable.lambda.monoid.builtin.LeftAll.leftAll; +import static org.junit.Assert.assertEquals; + +public class LeftAllTest { + + @Test + public void monoid() { + Monoid addition = Monoid.monoid((x, y) -> x + y, 0); + + assertEquals(left(0), leftAll(addition).identity()); + assertEquals(left(3), leftAll(addition, left(1), left(2))); + assertEquals(right("foo"), leftAll(addition, right("foo"), left(1))); + assertEquals(right("bar"), leftAll(addition, left(1), right("bar"))); + assertEquals(right("foo"), leftAll(addition, right("foo"), right("bar"))); + } + +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/LeftAnyTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/LeftAnyTest.java new file mode 100644 index 000000000..dc6692ed0 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/LeftAnyTest.java @@ -0,0 +1,24 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.monoid.Monoid; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.Either.left; +import static com.jnape.palatable.lambda.adt.Either.right; +import static com.jnape.palatable.lambda.monoid.builtin.LeftAny.leftAny; +import static org.junit.Assert.assertEquals; + +public class LeftAnyTest { + + @Test + public void monoid() { + LeftAny leftAny = leftAny(); + Monoid join = Monoid.monoid((x, y) -> x + y, ""); + + assertEquals(left(""), leftAny.apply(join).identity()); + assertEquals(left("foo"), leftAny.apply(join).apply(left("foo"), right(1))); + assertEquals(left("foo"), leftAny.apply(join).apply(right(1), left("foo"))); + assertEquals(left("foobar"), leftAny.apply(join).apply(left("foo"), left("bar"))); + assertEquals(right(2), leftAny.apply(join).apply(right(1), right(2))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/MergeTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/MergeTest.java new file mode 100644 index 000000000..2a7fd462d --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/MergeTest.java @@ -0,0 +1,38 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.adt.Either; +import com.jnape.palatable.lambda.monoid.Monoid; +import com.jnape.palatable.lambda.semigroup.Semigroup; +import org.junit.Before; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.Either.left; +import static com.jnape.palatable.lambda.adt.Either.right; +import static com.jnape.palatable.lambda.monoid.builtin.Merge.merge; +import static org.junit.Assert.assertEquals; + +public class MergeTest { + + private static final Semigroup JOIN = (x, y) -> x + y; + private static final Monoid ADD = Monoid.monoid((x, y) -> x + y, 0); + + private Monoid> merge; + + @Before + public void setUp() throws Exception { + merge = merge(JOIN, ADD); + } + + @Test + public void identity() { + assertEquals(right(0), merge.identity()); + } + + @Test + public void monoid() { + assertEquals(left("onetwo"), merge.apply(left("one"), left("two"))); + assertEquals(right(3), merge.apply(right(1), right(2))); + assertEquals(left("foo"), merge.apply(left("foo"), right(2))); + assertEquals(left("foo"), merge.apply(right(2), left("foo"))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/PresentTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/PresentTest.java new file mode 100644 index 000000000..058cd82ce --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/PresentTest.java @@ -0,0 +1,23 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.semigroup.Semigroup; +import org.junit.Test; + +import java.util.Optional; + +import static com.jnape.palatable.lambda.monoid.builtin.Present.present; +import static org.junit.Assert.assertEquals; + +public class PresentTest { + + @Test + public void monoid() { + Present present = present(); + Semigroup addition = (x, y) -> x + y; + + assertEquals(Optional.of(3), present.apply(addition, Optional.of(1), Optional.of(2))); + assertEquals(Optional.of(1), present.apply(addition, Optional.empty(), Optional.of(1))); + assertEquals(Optional.of(1), present.apply(addition, Optional.of(1), Optional.empty())); + assertEquals(Optional.empty(), present.apply(addition, Optional.empty(), Optional.empty())); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/PutAllTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/PutAllTest.java new file mode 100644 index 000000000..fc5500ac5 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/PutAllTest.java @@ -0,0 +1,32 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.adt.hmap.HMap; +import com.jnape.palatable.lambda.adt.hmap.TypeSafeKey; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hmap.HMap.emptyHMap; +import static com.jnape.palatable.lambda.adt.hmap.HMap.singletonHMap; +import static com.jnape.palatable.lambda.adt.hmap.TypeSafeKey.typeSafeKey; +import static com.jnape.palatable.lambda.monoid.builtin.PutAll.putAll; +import static org.junit.Assert.assertEquals; + +public class PutAllTest { + + @Test + public void identity() { + assertEquals(emptyHMap(), putAll().identity()); + } + + @Test + public void monoid() { + TypeSafeKey stringKey = typeSafeKey(); + TypeSafeKey intKey = typeSafeKey(); + + HMap x = singletonHMap(stringKey, "string"); + HMap y = singletonHMap(intKey, 1); + + HMap result = putAll(x, y); + assertEquals("string", result.demand(stringKey)); + assertEquals((Integer) 1, result.demand(intKey)); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/RightAllTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/RightAllTest.java new file mode 100644 index 000000000..d1789c8de --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/RightAllTest.java @@ -0,0 +1,24 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.monoid.Monoid; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.Either.left; +import static com.jnape.palatable.lambda.adt.Either.right; +import static com.jnape.palatable.lambda.monoid.builtin.RightAll.rightAll; +import static org.junit.Assert.assertEquals; + +public class RightAllTest { + + + @Test + public void monoid() { + final Monoid addition = Monoid.monoid((x, y) -> x + y, 0); + + assertEquals(right(0), rightAll(addition).identity()); + assertEquals(right(3), rightAll(addition, right(1), right(2))); + assertEquals(left("foo"), rightAll(addition, left("foo"), right(1))); + assertEquals(left("bar"), rightAll(addition, right(1), left("bar"))); + assertEquals(left("foo"), rightAll(addition, left("foo"), left("bar"))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/RightAnyTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/RightAnyTest.java new file mode 100644 index 000000000..96a4d4e8a --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/RightAnyTest.java @@ -0,0 +1,24 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.monoid.Monoid; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.Either.left; +import static com.jnape.palatable.lambda.adt.Either.right; +import static com.jnape.palatable.lambda.monoid.builtin.RightAny.rightAny; +import static org.junit.Assert.assertEquals; + +public class RightAnyTest { + + @Test + public void monoid() { + RightAny rightAny = rightAny(); + Monoid add = Monoid.monoid((x, y) -> x + y, 0); + + assertEquals(right(0), rightAny.apply(add).identity()); + assertEquals(right(1), rightAny.apply(add).apply(right(1), left("foo"))); + assertEquals(right(1), rightAny.apply(add).apply(left("foo"), right(1))); + assertEquals(right(3), rightAny.apply(add).apply(right(1), right(2))); + assertEquals(left("bar"), rightAny.apply(add).apply(left("foo"), left("bar"))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/semigroup/LeftTest.java b/src/test/java/com/jnape/palatable/lambda/semigroup/LeftTest.java deleted file mode 100644 index 0d2d65e75..000000000 --- a/src/test/java/com/jnape/palatable/lambda/semigroup/LeftTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.jnape.palatable.lambda.semigroup; - -import org.junit.Test; - -import static com.jnape.palatable.lambda.adt.Either.left; -import static com.jnape.palatable.lambda.adt.Either.right; -import static org.junit.Assert.assertEquals; - -public class LeftTest { - private static final Semigroup ADDITION = (x, y) -> x + y; - - @Test - public void appliesTwoLeftValues() { - assertEquals(left(3), new Left<>(ADDITION).apply(left(1), left(2))); - } - - @Test - public void rightValueSubvertsSemigroup() { - assertEquals(right("foo"), new Left<>(ADDITION).apply(right("foo"), left(1))); - assertEquals(right("bar"), new Left<>(ADDITION).apply(left(1), right("bar"))); - assertEquals(right("foo"), new Left<>(ADDITION).apply(right("foo"), right("bar"))); - } -} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/semigroup/PresentTest.java b/src/test/java/com/jnape/palatable/lambda/semigroup/PresentTest.java deleted file mode 100644 index 0f1858b99..000000000 --- a/src/test/java/com/jnape/palatable/lambda/semigroup/PresentTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.jnape.palatable.lambda.semigroup; - -import org.junit.Test; - -import java.util.Optional; - -import static org.junit.Assert.assertEquals; - -public class PresentTest { - - private static final Semigroup ADDITION = (x, y) -> x + y; - - @Test - public void appliesTwoPresentValues() { - assertEquals(Optional.of(3), new Present<>(ADDITION).apply(Optional.of(1), Optional.of(2))); - } - - @Test - public void emptyValueSubvertsSemigroup() { - assertEquals(Optional.empty(), new Present<>(ADDITION).apply(Optional.empty(), Optional.of(1))); - assertEquals(Optional.empty(), new Present<>(ADDITION).apply(Optional.of(1), Optional.empty())); - assertEquals(Optional.empty(), new Present<>(ADDITION).apply(Optional.empty(), Optional.empty())); - } -} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/semigroup/RightTest.java b/src/test/java/com/jnape/palatable/lambda/semigroup/RightTest.java deleted file mode 100644 index d31ca130c..000000000 --- a/src/test/java/com/jnape/palatable/lambda/semigroup/RightTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.jnape.palatable.lambda.semigroup; - -import org.junit.Test; - -import static com.jnape.palatable.lambda.adt.Either.left; -import static com.jnape.palatable.lambda.adt.Either.right; -import static org.junit.Assert.assertEquals; - -public class RightTest { - private static final Semigroup ADDITION = (x, y) -> x + y; - - @Test - public void appliesTwoRightValues() { - assertEquals(right(3), new Right<>(ADDITION).apply(right(1), right(2))); - } - - @Test - public void leftValueSubvertsSemigroup() { - assertEquals(left("foo"), new Right<>(ADDITION).apply(left("foo"), right(1))); - assertEquals(left("bar"), new Right<>(ADDITION).apply(right(1), left("bar"))); - assertEquals(left("foo"), new Right<>(ADDITION).apply(left("foo"), left("bar"))); - } -} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/semigroup/SemigroupTest.java b/src/test/java/com/jnape/palatable/lambda/semigroup/SemigroupTest.java index ec20057bc..94697cda3 100644 --- a/src/test/java/com/jnape/palatable/lambda/semigroup/SemigroupTest.java +++ b/src/test/java/com/jnape/palatable/lambda/semigroup/SemigroupTest.java @@ -1,5 +1,6 @@ package com.jnape.palatable.lambda.semigroup; +import com.jnape.palatable.lambda.semigroup.Semigroup; import org.junit.Test; import static java.util.Arrays.asList; diff --git a/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/CollapseTest.java b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/CollapseTest.java new file mode 100644 index 000000000..face88111 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/CollapseTest.java @@ -0,0 +1,20 @@ +package com.jnape.palatable.lambda.semigroup.builtin; + +import com.jnape.palatable.lambda.semigroup.Semigroup; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.semigroup.builtin.Collapse.collapse; +import static org.junit.Assert.assertEquals; + +public class CollapseTest { + + @Test + public void semigroup() { + Semigroup join = (x, y) -> x + y; + Semigroup add = (x, y) -> x + y; + + Collapse collapse = collapse(); + assertEquals(tuple("foobar", 3), collapse.apply(join, add, tuple("foo", 1), tuple("bar", 2))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/ConcatTest.java b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/ConcatTest.java new file mode 100644 index 000000000..e712c8c0d --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/ConcatTest.java @@ -0,0 +1,23 @@ +package com.jnape.palatable.lambda.semigroup.builtin; + +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; + +import static com.jnape.palatable.lambda.semigroup.builtin.Concat.concat; +import static java.util.Collections.singleton; +import static org.junit.Assert.assertEquals; + +public class ConcatTest { + + @Test + public void semigroup() { + Concat> concat = concat(); + + assertEquals(new HashSet() {{ + add(1); + add(2); + }}, concat.apply(new HashSet<>(singleton(1)), new HashSet<>(singleton(2)))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/LeftAllTest.java b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/LeftAllTest.java new file mode 100644 index 000000000..8ae68ecc5 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/LeftAllTest.java @@ -0,0 +1,22 @@ +package com.jnape.palatable.lambda.semigroup.builtin; + +import com.jnape.palatable.lambda.semigroup.Semigroup; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.Either.left; +import static com.jnape.palatable.lambda.adt.Either.right; +import static com.jnape.palatable.lambda.semigroup.builtin.LeftAll.leftAll; +import static org.junit.Assert.assertEquals; + +public class LeftAllTest { + + @Test + public void semigroup() { + Semigroup addition = (x, y) -> x + y; + + assertEquals(left(3), leftAll(addition, left(1), left(2))); + assertEquals(right("foo"), leftAll(addition, right("foo"), left(1))); + assertEquals(right("bar"), leftAll(addition, left(1), right("bar"))); + assertEquals(right("foo"), leftAll(addition, right("foo"), right("bar"))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/LeftAnyTest.java b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/LeftAnyTest.java new file mode 100644 index 000000000..98026177b --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/LeftAnyTest.java @@ -0,0 +1,23 @@ +package com.jnape.palatable.lambda.semigroup.builtin; + +import com.jnape.palatable.lambda.semigroup.Semigroup; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.Either.left; +import static com.jnape.palatable.lambda.adt.Either.right; +import static com.jnape.palatable.lambda.semigroup.builtin.LeftAny.leftAny; +import static org.junit.Assert.assertEquals; + +public class LeftAnyTest { + + @Test + public void semigroup() { + LeftAny leftAny = leftAny(); + Semigroup join = (x, y) -> x + y; + + assertEquals(left("foo"), leftAny.apply(join).apply(left("foo"), right(1))); + assertEquals(left("foo"), leftAny.apply(join).apply(right(1), left("foo"))); + assertEquals(left("foobar"), leftAny.apply(join).apply(left("foo"), left("bar"))); + assertEquals(right(2), leftAny.apply(join).apply(right(1), right(2))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/MergeTest.java b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/MergeTest.java new file mode 100644 index 000000000..c867c1302 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/MergeTest.java @@ -0,0 +1,25 @@ +package com.jnape.palatable.lambda.semigroup.builtin; + +import com.jnape.palatable.lambda.adt.Either; +import com.jnape.palatable.lambda.semigroup.Semigroup; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.Either.left; +import static com.jnape.palatable.lambda.adt.Either.right; +import static com.jnape.palatable.lambda.semigroup.builtin.Merge.merge; +import static org.junit.Assert.assertEquals; + +public class MergeTest { + + @Test + public void semigroup() { + Semigroup join = (x, y) -> x + y; + Semigroup add = (x, y) -> x + y; + + Semigroup> merge = merge(join, add); + assertEquals(left("onetwo"), merge.apply(left("one"), left("two"))); + assertEquals(right(3), merge.apply(right(1), right(2))); + assertEquals(left("foo"), merge.apply(left("foo"), right(2))); + assertEquals(left("foo"), merge.apply(right(2), left("foo"))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/RightAllTest.java b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/RightAllTest.java new file mode 100644 index 000000000..4f33db53f --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/RightAllTest.java @@ -0,0 +1,22 @@ +package com.jnape.palatable.lambda.semigroup.builtin; + +import com.jnape.palatable.lambda.semigroup.Semigroup; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.Either.left; +import static com.jnape.palatable.lambda.adt.Either.right; +import static com.jnape.palatable.lambda.semigroup.builtin.RightAll.rightAll; +import static org.junit.Assert.assertEquals; + +public class RightAllTest { + + @Test + public void semigroup() { + Semigroup addition = (x, y) -> x + y; + + assertEquals(right(3), rightAll(addition).apply(right(1), right(2))); + assertEquals(left("foo"), rightAll(addition).apply(left("foo"), right(1))); + assertEquals(left("bar"), rightAll(addition).apply(right(1), left("bar"))); + assertEquals(left("foo"), rightAll(addition).apply(left("foo"), left("bar"))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/RightAnyTest.java b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/RightAnyTest.java new file mode 100644 index 000000000..ec234090e --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/semigroup/builtin/RightAnyTest.java @@ -0,0 +1,23 @@ +package com.jnape.palatable.lambda.semigroup.builtin; + +import com.jnape.palatable.lambda.semigroup.Semigroup; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.Either.left; +import static com.jnape.palatable.lambda.adt.Either.right; +import static com.jnape.palatable.lambda.semigroup.builtin.RightAny.rightAny; +import static org.junit.Assert.assertEquals; + +public class RightAnyTest { + + @Test + public void semigroup() { + RightAny rightAny = rightAny(); + Semigroup add = (x, y) -> x + y; + + assertEquals(right(1), rightAny.apply(add).apply(right(1), left("foo"))); + assertEquals(right(1), rightAny.apply(add).apply(left("foo"), right(1))); + assertEquals(right(3), rightAny.apply(add).apply(right(1), right(2))); + assertEquals(left("bar"), rightAny.apply(add).apply(left("foo"), left("bar"))); + } +} \ No newline at end of file From 43183cc72b04deadb963fb946dbc28aafe9e59b5 Mon Sep 17 00:00:00 2001 From: jnape Date: Sat, 17 Dec 2016 13:29:01 -0600 Subject: [PATCH 8/8] [maven-release-plugin] prepare release lambda-1.5.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0a32ad524..67c73e480 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ lambda - 1.5.5-SNAPSHOT + 1.5.5 jar Lambda