From c7dcb2a48cf86a07919415de7d5c88230f3d158a Mon Sep 17 00:00:00 2001 From: jnape Date: Tue, 29 Dec 2015 18:20:17 -0600 Subject: [PATCH 01/18] [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 d268b8210..97a6c49c6 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ lambda - 1.0 + 1.1-SNAPSHOT jar Lambda From 43ea5bd618ce4d1a065b687819f2b854577861bf Mon Sep 17 00:00:00 2001 From: John Napier Date: Wed, 30 Dec 2015 00:25:19 -0600 Subject: [PATCH 02/18] Update README.md --- README.md | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 124 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dfc2cbf17..3067bbd88 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,127 @@ lambda ====== -Functional patterns for Java 8 \ No newline at end of file +Functional patterns for Java 8 + +Installation +------------ + +Add the following dependency to your: + +`pom.xml` ([Maven](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html)): + + ```xml + + com.jnape.palatable + lambda + 1.0 + + ``` + + `build.gradle` ([Gradle](https://docs.gradle.org/current/userguide/dependency_management.html)): + + ```gradle + compile group: 'com.jnape.palatable', name: 'lambda', version: '1.0' + ``` + +Background +---------- + +Lambda was born out of a desire to use some of the same canonical functions (e.g. `unfoldr`, `takeWhile`, `zipWith`) and functional patterns (e.g. `Functor` and friends) that are idiomatic in other languages and make them available for Java. + +Some things a user of lambda most likely values: + +- Lazy evaluation +- Immutablility by design +- Composition +- Higher-level abstractions +- Parametric polymorphism + +Generally, everything that lambda produces is lazily-evaluated (except for terminal operations like `reduce`), immutable (except for `Iterator`s, since it's effectively impossible), composable (even between different arities, where possible), foundational (maximally contravariant), and parametrically type-checked (even where this adds unnecessary constraints due to a lack of higher-kinded types). + +Although the library is currently (very) small, these values should always be the driving forces behind future growth. + +Examples +-------- + +First, the obligatory `map`/`filter`/`reduce` example: +```Java + Integer sumOfEvenIncrements = + reduceLeft((x, y) -> x + y, + filter(x -> x % 2 == 0, + map(x -> x + 1, asList(1, 2, 3, 4, 5)))); + //-> 12 +``` + +Every function in lambda is [curried](https://www.wikiwand.com/en/Currying), so we could have also done this: +```Java + MonadicFunction, Integer> sumOfEvenIncrementsFn = + map((Integer x) -> x + 1) + .then(filter(x -> x % 2 == 0)) + .then(reduceLeft((x, y) -> x + y)); + + Integer sumOfEvenIncrements = sumOfEvenIncrementsFn.apply(asList(1, 2, 3, 4, 5)); + //-> 12 +``` + +How about the positive squares below 100: + +```Java + Iterable positiveSquaresBelow100 = + takeWhile(x -> x < 100, map(x -> x * x, iterate(x -> x + 1, 1))); + //-> [1, 4, 9, 16, 25, 36, 49, 64, 81] +``` + +We could have also used `unfoldr`: + +```Java + Iterable positiveSquaresBelow100 = unfoldr(x -> { + int square = x * x; + return square < 100 ? Optional.of(tuple(square, x + 1)) : Optional.empty(); + }, 1); + //-> [1, 4, 9, 16, 25, 36, 49, 64, 81] +``` + +What if we want the cross product of a domain and codomain: + +```Java + Iterable> crossProduct = + take(10, cartesianProduct(asList(1, 2, 3), asList("a", "b", "c"))); + //-> (1,"a"), (1,"b"), (1,"c"), (2,"a"), (2,"b"), (2,"c"), (3,"a"), (3,"b"), (3,"c") +``` + +Let's compose two functions: + +```Java + MonadicFunction add = x -> x + 1; + MonadicFunction subtract = x -> x -1; + + MonadicFunction noOp = add.then(subtract); + // same as + MonadicFunction alsoNoOp = subtract.fmap(add); +``` + +And partially apply some: + +```Java + DyadicFunction add = (x, y) -> x + y; + + MonadicFunction add1 = add.apply(1); + add1.apply(2); + //-> 3 +``` + +And have fun with 3s: + +```Java + Iterable> multiplesOf3InGroupsOf3 = + take(10, inGroupsOf(3, unfoldr(x -> Optional.of(tuple(x * 3, x + 1)), 1))); + //-> [[3, 6, 9], [12, 15, 18], [21, 24, 27]] +``` + +Or check out [the tests](https://github.com/palatable/lambda/tree/master/src/test/java/com/jnape/palatable/lambda/functions/builtin) for more examples. + +license +------- + +_lambda_ is part of [palatable](http://www.github.com/palatable), which is distributed under [The MIT License](http://choosealicense.com/licenses/mit/). From 736a437c39ae645d2cb55eead2f8dae9dccafd86 Mon Sep 17 00:00:00 2001 From: jnape Date: Wed, 30 Dec 2015 00:32:55 -0600 Subject: [PATCH 03/18] adding travis yml --- .travis.yml | 3 +++ pom.xml | 20 ++++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..9bcf99945 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: java +jdk: + - oraclejdk8 diff --git a/pom.xml b/pom.xml index 97a6c49c6..76e98765b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 @@ -45,6 +46,7 @@ 3.1 1.0 1.0 + 3.3 @@ -74,4 +76,18 @@ - \ No newline at end of file + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 1.8 + 1.8 + + + + + + From 8740db3543241982bdaf7ef1922405490c350722 Mon Sep 17 00:00:00 2001 From: John Napier Date: Wed, 30 Dec 2015 18:58:09 -0600 Subject: [PATCH 04/18] Adding TravisCI badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3067bbd88..f4a4c4ec5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ lambda ====== +[![Build Status](https://travis-ci.org/palatable/lambda.svg)](https://travis-ci.org/palatable/lambda) Functional patterns for Java 8 From 033006d03f0fc5a62252f72f8b67d95869bc3aaf Mon Sep 17 00:00:00 2001 From: jnape Date: Wed, 30 Dec 2015 19:53:13 -0600 Subject: [PATCH 05/18] Adding scanLeft; cleaning up dependencies --- pom.xml | 12 +++--- .../functions/builtin/triadic/ScanLeft.java | 34 +++++++++++++++ .../lambda/iterators/ScanningIterator.java | 35 +++++++++++++++ .../builtin/triadic/ScanLeftTest.java | 42 ++++++++++++++++++ .../iterators/ScanningIteratorTest.java | 43 +++++++++++++++++++ 5 files changed, 161 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/ScanLeft.java create mode 100644 src/main/java/com/jnape/palatable/lambda/iterators/ScanningIterator.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ScanLeftTest.java create mode 100644 src/test/java/com/jnape/palatable/lambda/iterators/ScanningIteratorTest.java diff --git a/pom.xml b/pom.xml index 76e98765b..e2bd2db65 100644 --- a/pom.xml +++ b/pom.xml @@ -58,15 +58,17 @@ org.mockito mockito-all - - com.jnape.palatable - traitor - ${traitor.version} - com.jnape.loan-shark loan-shark ${loan-shark.version} + provided + + + com.jnape.palatable + traitor + ${traitor.version} + test org.apache.commons diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/ScanLeft.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/ScanLeft.java new file mode 100644 index 000000000..d6d16e722 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/ScanLeft.java @@ -0,0 +1,34 @@ +package com.jnape.palatable.lambda.functions.builtin.triadic; + +import com.jnape.palatable.lambda.functions.DyadicFunction; +import com.jnape.palatable.lambda.functions.MonadicFunction; +import com.jnape.palatable.lambda.functions.TriadicFunction; +import com.jnape.palatable.lambda.iterators.ScanningIterator; + +public final class ScanLeft implements TriadicFunction, B, Iterable, Iterable> { + + @Override + public Iterable apply(DyadicFunction fn, B b, Iterable as) { + return () -> new ScanningIterator<>(fn, b, as.iterator()); + } + + public static ScanLeft scanLeft() { + return new ScanLeft<>(); + } + + public static DyadicFunction, Iterable> scanLeft( + DyadicFunction fn) { + return ScanLeft.scanLeft().apply(fn); + } + + public static MonadicFunction, Iterable> scanLeft( + DyadicFunction fn, B b) { + return scanLeft(fn).apply(b); + } + + public static Iterable scanLeft(DyadicFunction fn, + B b, + Iterable as) { + return scanLeft(fn, b).apply(as); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/iterators/ScanningIterator.java b/src/main/java/com/jnape/palatable/lambda/iterators/ScanningIterator.java new file mode 100644 index 000000000..085ba592c --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/iterators/ScanningIterator.java @@ -0,0 +1,35 @@ +package com.jnape.palatable.lambda.iterators; + +import com.jnape.palatable.lambda.functions.DyadicFunction; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +public final class ScanningIterator extends ImmutableIterator { + + private final DyadicFunction scanner; + private final Iterator asIterator; + private B b; + + public ScanningIterator(DyadicFunction scanner, B b, + Iterator asIterator) { + this.scanner = scanner; + this.b = b; + this.asIterator = asIterator; + } + + @Override + public boolean hasNext() { + return b != null; + } + + @Override + public B next() { + if (b == null) + throw new NoSuchElementException(); + + B next = b; + b = asIterator.hasNext() ? scanner.apply(b, asIterator.next()) : null; + return next; + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ScanLeftTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ScanLeftTest.java new file mode 100644 index 000000000..621f94fc4 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ScanLeftTest.java @@ -0,0 +1,42 @@ +package com.jnape.palatable.lambda.functions.builtin.triadic; + +import com.jnape.palatable.lambda.functions.MonadicFunction; +import com.jnape.palatable.traitor.annotations.TestTraits; +import com.jnape.palatable.traitor.runners.Traits; +import org.junit.Test; +import org.junit.runner.RunWith; +import testsupport.traits.EmptyIterableSupport; +import testsupport.traits.FiniteIteration; +import testsupport.traits.Laziness; + +import java.util.Collections; + +import static com.jnape.palatable.lambda.functions.builtin.triadic.ScanLeft.scanLeft; +import static java.util.Arrays.asList; +import static org.junit.Assert.assertThat; +import static testsupport.matchers.IterableMatcher.iterates; + +@RunWith(Traits.class) +public class ScanLeftTest { + + @TestTraits({EmptyIterableSupport.class, FiniteIteration.class, Laziness.class}) + public MonadicFunction, Iterable> createTestSubject() { + return scanLeft((x, y) -> x, new Object()); + } + + @Test + public void buildsUpContinuationOfIncrementalAccumulations() { + assertThat( + scanLeft((x, y) -> x + y, 0, asList(1, 2, 3, 4, 5)), + iterates(0, 1, 3, 6, 10, 15) + ); + } + + @Test + public void initialAccumulationIsOnlyResultIfEmptyIterable() { + assertThat( + scanLeft((x, y) -> x + y, 0, Collections.emptyList()), + iterates(0) + ); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/iterators/ScanningIteratorTest.java b/src/test/java/com/jnape/palatable/lambda/iterators/ScanningIteratorTest.java new file mode 100644 index 000000000..ed93c0042 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/iterators/ScanningIteratorTest.java @@ -0,0 +1,43 @@ +package com.jnape.palatable.lambda.iterators; + +import org.junit.Test; + +import java.util.NoSuchElementException; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyIterator; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +public class ScanningIteratorTest { + + public static final com.jnape.palatable.lambda.functions.DyadicFunction ADD = (x, y) -> x + y; + + @Test + public void hasNextAtLeastForB() { + ScanningIterator scanningIterator = new ScanningIterator<>(ADD, 0, emptyIterator()); + assertThat(scanningIterator.hasNext(), is(true)); + } + + @Test + public void nextIsTrueAtLeastForB() { + ScanningIterator scanningIterator = new ScanningIterator<>(ADD, 0, emptyIterator()); + assertThat(scanningIterator.next(), is(0)); + } + + @Test + public void iteratesAsAfterB() { + ScanningIterator scanningIterator = new ScanningIterator<>(ADD, 0, asList(1, 2, 3, 4, 5).iterator()); + + scanningIterator.next(); + assertThat(scanningIterator.hasNext(), is(true)); + assertThat(scanningIterator.next(), is(ADD.apply(0, 1))); + } + + @Test(expected = NoSuchElementException.class) + public void failsOnNextIfOutOfAs() { + ScanningIterator scanningIterator = new ScanningIterator<>(ADD, 0, emptyIterator()); + scanningIterator.next(); + scanningIterator.next(); + } +} \ No newline at end of file From 79e7ac2730af69022fa4af574a76b8c80ccebf68 Mon Sep 17 00:00:00 2001 From: jnape Date: Thu, 14 Jan 2016 01:15:54 -0600 Subject: [PATCH 06/18] Adding Either, a BiFunctor supporting type intersections - removed LoanShark dependency - cleaned up spacing in README - added adt package, and moving tuples under that - BiFunctor now extends Functor as it should --- README.md | 72 ++++++- pom.xml | 7 - .../jnape/palatable/lambda/adt/Either.java | 198 ++++++++++++++++++ .../palatable/lambda/adt/tuples/Tuple2.java | 63 ++++++ .../palatable/lambda/adt/tuples/Tuple3.java | 61 ++++++ .../lambda/applicative/BiFunctor.java | 7 +- .../lambda/continuation/Continuation.java | 4 +- .../continuation/ContinuationIterator.java | 2 +- .../IteratorWrappingContinuation.java | 4 +- .../lambda/functions/DyadicFunction.java | 2 +- .../lambda/functions/TriadicFunction.java | 2 +- .../builtin/dyadic/CartesianProduct.java | 2 +- .../functions/builtin/dyadic/Iterate.java | 2 +- .../functions/builtin/dyadic/Tupler2.java | 4 +- .../functions/builtin/dyadic/Unfoldr.java | 2 +- .../lambda/functions/builtin/dyadic/Zip.java | 2 +- .../iterators/CombinatorialIterator.java | 4 +- .../lambda/iterators/UnfoldingIterator.java | 2 +- .../jnape/palatable/lambda/tuples/Tuple2.java | 57 ----- .../jnape/palatable/lambda/tuples/Tuple3.java | 22 -- .../palatable/lambda/adt/EitherTest.java | 124 +++++++++++ .../lambda/{ => adt}/tuples/Tuple2Test.java | 8 +- .../lambda/{ => adt}/tuples/Tuple3Test.java | 4 +- .../lambda/applicative/BiFunctorTest.java | 11 + .../lambda/continuation/ContinuationTest.java | 2 +- .../lambda/functions/DyadicFunctionTest.java | 4 +- .../lambda/functions/TriadicFunctionTest.java | 2 +- .../builtin/dyadic/CartesianProductTest.java | 4 +- .../functions/builtin/dyadic/Tupler2Test.java | 2 +- .../functions/builtin/dyadic/UnfoldrTest.java | 2 +- .../functions/builtin/dyadic/ZipTest.java | 4 +- .../builtin/triadic/ZipWithTest.java | 4 +- .../iterators/CombinatorialIteratorTest.java | 2 +- .../iterators/UnfoldingIteratorTest.java | 4 +- 34 files changed, 566 insertions(+), 130 deletions(-) create mode 100644 src/main/java/com/jnape/palatable/lambda/adt/Either.java create mode 100644 src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple2.java create mode 100644 src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple3.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/tuples/Tuple2.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/tuples/Tuple3.java create mode 100644 src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java rename src/test/java/com/jnape/palatable/lambda/{ => adt}/tuples/Tuple2Test.java (89%) rename src/test/java/com/jnape/palatable/lambda/{ => adt}/tuples/Tuple3Test.java (84%) diff --git a/README.md b/README.md index f4a4c4ec5..9d819fb8f 100644 --- a/README.md +++ b/README.md @@ -11,19 +11,19 @@ Add the following dependency to your: `pom.xml` ([Maven](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html)): - ```xml +```xml com.jnape.palatable lambda 1.0 - ``` +``` - `build.gradle` ([Gradle](https://docs.gradle.org/current/userguide/dependency_management.html)): +`build.gradle` ([Gradle](https://docs.gradle.org/current/userguide/dependency_management.html)): - ```gradle +```gradle compile group: 'com.jnape.palatable', name: 'lambda', version: '1.0' - ``` +``` Background ---------- @@ -122,7 +122,67 @@ And have fun with 3s: Or check out [the tests](https://github.com/palatable/lambda/tree/master/src/test/java/com/jnape/palatable/lambda/functions/builtin) for more examples. -license +ADTs +---- + +In addition to the functions above, Lambda also supports a few first-class [algebraic data types](https://www.wikiwand.com/en/Algebraic_data_type). + +### Tuples + +There are two parametric variants on the tuple product type, `Tuple2<_1, _2>` and `Tuple3<_1, _2, _3>`, both immutable. + +```Java + Tuple2 tuple2 = Tuple2.tuple("foo", 1); + + System.out.println(tuple2._1); // prints "foo" + System.out.println(tuple2._2); // prints 1 +``` + +```Java + Tuple3 tuple3 = Tuple3.tuple("foo", true, 1); + + System.out.println(tuple3._1); // prints "foo" + System.out.println(tuple3._2); // prints true + System.out.println(tuple3._3); // prints 1 +``` + +Both `Tuple2` and `Tuple3` are `BiFunctor`s over `_1` and `_2`: + +```Java + Tuple2 mappedTuple2 = tuple2.biMap(String::toUpperCase, x -> x + 1); + + System.out.println(mappedTuple2._1); // prints "FOO" + System.out.println(mappedTuple2._2); // prints 2 +``` + +```Java + Tuple3 mappedTuple3 = tuple3.biMap(String::toUpperCase, b -> !b); + + System.out.println(mappedTuple3._1); // prints "FOO" + System.out.println(mappedTuple3._2); // prints false + System.out.println(mappedTuple3._3); // prints 1 +``` + +### Either + +Binary tagged unions are represented as `Either`s, which resolve to one of two possible values: a `Left` value wrapping an `L`, or a `Right` value wrapping an `R` (typically an exceptional value or a successful value, respectively). + +Rather than supporting explicit value unwrapping, `Either` supports many useful comprehensions to help facilitate type-safe interactions. For example, `Either#match` is used to resolve an `Either` to a different type. + +```Java + Either right = Either.right(1); + Either left = Either.left("Head fell off"); + + Boolean successful = right.match(l -> false, r -> true); + //-> true + + List values = left.match(l -> Collections.emptyList(), Collections::singletonList); + //-> [] +``` + +Check out the tests for [more examples](https://github.com/palatable/lambda/blob/master/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java) of ways to interact with `Either`. + +License ------- _lambda_ is part of [palatable](http://www.github.com/palatable), which is distributed under [The MIT License](http://choosealicense.com/licenses/mit/). diff --git a/pom.xml b/pom.xml index e2bd2db65..8d10ef325 100644 --- a/pom.xml +++ b/pom.xml @@ -44,7 +44,6 @@ 3.1 - 1.0 1.0 3.3 @@ -58,12 +57,6 @@ org.mockito mockito-all - - com.jnape.loan-shark - loan-shark - ${loan-shark.version} - provided - com.jnape.palatable traitor diff --git a/src/main/java/com/jnape/palatable/lambda/adt/Either.java b/src/main/java/com/jnape/palatable/lambda/adt/Either.java new file mode 100644 index 000000000..475ebffed --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/Either.java @@ -0,0 +1,198 @@ +package com.jnape.palatable.lambda.adt; + +import com.jnape.palatable.lambda.applicative.BiFunctor; +import com.jnape.palatable.lambda.applicative.Functor; +import com.jnape.palatable.lambda.functions.DyadicFunction; +import com.jnape.palatable.lambda.functions.MonadicFunction; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; + +public interface Either extends Functor, BiFunctor { + + static Either either(Optional optional, Supplier leftFn) { + return optional.>map(Either::right) + .orElse(left(leftFn.get())); + } + + default R or(R defaultValue) { + return recover(l -> defaultValue); + } + + R recover(MonadicFunction recoveryFn); + + R orThrow(MonadicFunction throwableFn) throws E; + + Either filter(MonadicFunction pred, + Supplier leftSupplier); + + Either flatMap(MonadicFunction> leftFn, + MonadicFunction> rightFn); + + Either merge(DyadicFunction leftFn, + DyadicFunction rightFn, + Either other); + + V match(MonadicFunction leftFn, + MonadicFunction rightFn); + + @Override + default Either fmap(MonadicFunction fn) { + return biMapR(fn); + } + + @Override + default Either biMapL(MonadicFunction fn) { + return (Either) BiFunctor.super.biMapL(fn); + } + + @Override + default Either biMapR(MonadicFunction fn) { + return (Either) BiFunctor.super.biMapR(fn); + } + + @Override + Either biMap(MonadicFunction leftFn, + MonadicFunction rightFn); + + static Either left(L l) { + return new Left<>(l); + } + + static Either right(R r) { + return new Right<>(r); + } + + final class Left implements Either { + private final L l; + + private Left(L l) { + this.l = l; + } + + @Override + public R recover(MonadicFunction recoveryFn) { + return recoveryFn.apply(l); + } + + @Override + public R orThrow(MonadicFunction throwableFn) throws E { + throw throwableFn.apply(l); + } + + @Override + public Either filter(MonadicFunction pred, Supplier leftSupplier) { + return this; + } + + @Override + public Either flatMap(MonadicFunction> leftFn, + MonadicFunction> rightFn) { + return leftFn.apply(l); + } + + @Override + public Either merge(DyadicFunction leftFn, + DyadicFunction rightFn, Either other) { + return other instanceof Left + ? left(leftFn.apply(l, ((Left) other).l)) + : this; + } + + @Override + public V match(MonadicFunction leftFn, + MonadicFunction rightFn) { + return leftFn.apply(l); + } + + @Override + public Either biMap(MonadicFunction leftFn, + MonadicFunction rightFn) { + return left(leftFn.apply(l)); + } + + @Override + public boolean equals(Object other) { + return other instanceof Left && Objects.equals(l, ((Left) other).l); + } + + @Override + public int hashCode() { + return Objects.hash(l); + } + + @Override + public String toString() { + return "Left{" + + "l=" + l + + '}'; + } + } + + final class Right implements Either { + private final R r; + + private Right(R r) { + this.r = r; + } + + @Override + public R recover(MonadicFunction recoveryFn) { + return r; + } + + @Override + public R orThrow(MonadicFunction throwableFn) throws E { + return r; + } + + @Override + public Either filter(MonadicFunction pred, Supplier leftSupplier) { + return pred.apply(r) ? this : left(leftSupplier.get()); + } + + @Override + public Either flatMap(MonadicFunction> leftFn, + MonadicFunction> rightFn) { + return rightFn.apply(r); + } + + @Override + public Either merge(DyadicFunction leftFn, + DyadicFunction rightFn, Either other) { + return other instanceof Right + ? right(rightFn.apply(r, ((Right) other).r)) + : other; + } + + @Override + public V match(MonadicFunction leftFn, + MonadicFunction rightFn) { + return rightFn.apply(r); + } + + @Override + public Either biMap(MonadicFunction leftFn, + MonadicFunction rightFn) { + return right(rightFn.apply(r)); + } + + @Override + public boolean equals(Object other) { + return other instanceof Right && Objects.equals(r, ((Right) other).r); + } + + @Override + public int hashCode() { + return Objects.hash(r); + } + + @Override + public String toString() { + return "Right{" + + "r=" + r + + '}'; + } + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple2.java b/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple2.java new file mode 100644 index 000000000..eb939ff8e --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple2.java @@ -0,0 +1,63 @@ +package com.jnape.palatable.lambda.adt.tuples; + +import com.jnape.palatable.lambda.applicative.BiFunctor; +import com.jnape.palatable.lambda.functions.MonadicFunction; + +import java.util.Objects; + +import static java.lang.String.format; + +public class Tuple2<_1, _2> implements BiFunctor<_1, _2> { + + public final _1 _1; + public final _2 _2; + + public Tuple2(_1 _1, _2 _2) { + this._1 = _1; + this._2 = _2; + } + + @Override + public <_2A> Tuple2<_1, _2A> fmap(MonadicFunction fn) { + return (Tuple2<_1, _2A>) BiFunctor.super.fmap(fn); + } + + @Override + public <_1A, _2A> Tuple2<_1A, _2A> biMap(MonadicFunction f1, + MonadicFunction f2) { + return tuple(f1.apply(_1), f2.apply(_2)); + } + + @Override + public <_1A> Tuple2<_1A, _2> biMapL(MonadicFunction fn) { + return (Tuple2<_1A, _2>) BiFunctor.super.biMapL(fn); + } + + @Override + public <_2A> Tuple2<_1, _2A> biMapR(MonadicFunction fn) { + return (Tuple2<_1, _2A>) BiFunctor.super.biMapR(fn); + } + + @Override + public String toString() { + return format("(%s, %s)", _1, _2); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Tuple2 tuple2 = (Tuple2) o; + return Objects.equals(_1, tuple2._1) && + Objects.equals(_2, tuple2._2); + } + + @Override + public int hashCode() { + return Objects.hash(_1, _2); + } + + public static <_1, _2> Tuple2<_1, _2> tuple(_1 _1, _2 _2) { + return new Tuple2<>(_1, _2); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple3.java b/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple3.java new file mode 100644 index 000000000..8a2d7c886 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple3.java @@ -0,0 +1,61 @@ +package com.jnape.palatable.lambda.adt.tuples; + +import com.jnape.palatable.lambda.functions.MonadicFunction; + +import java.util.Objects; + +import static java.lang.String.format; + +public class Tuple3<_1, _2, _3> extends Tuple2<_1, _2> { + + public final _3 _3; + + public Tuple3(_1 _1, _2 _2, _3 _3) { + super(_1, _2); + this._3 = _3; + } + + @Override + public <_2A> Tuple3<_1, _2A, _3> fmap(MonadicFunction fn) { + return (Tuple3<_1, _2A, _3>) super.fmap(fn); + } + + @Override + public <_1A, _2A> Tuple3<_1A, _2A, _3> biMap(MonadicFunction f1, + MonadicFunction f2) { + return (Tuple3<_1A, _2A, _3>) super.biMap(f1, f2); + } + + @Override + public <_1A> Tuple3<_1A, _2, _3> biMapL(MonadicFunction fn) { + return (Tuple3<_1A, _2, _3>) super.biMapL(fn); + } + + @Override + public <_2A> Tuple3<_1, _2A, _3> biMapR(MonadicFunction fn) { + return (Tuple3<_1, _2A, _3>) super.biMapR(fn); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + Tuple3 tuple3 = (Tuple3) o; + return Objects.equals(_3, tuple3._3); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), _3); + } + + @Override + public String toString() { + return format("(%s, %s, %s)", _1, _2, _3); + } + + public static <_1, _2, _3> Tuple3<_1, _2, _3> tuple(_1 _1, _2 _2, _3 _3) { + return new Tuple3<>(_1, _2, _3); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/applicative/BiFunctor.java b/src/main/java/com/jnape/palatable/lambda/applicative/BiFunctor.java index 1a63dd236..c3f2650c4 100644 --- a/src/main/java/com/jnape/palatable/lambda/applicative/BiFunctor.java +++ b/src/main/java/com/jnape/palatable/lambda/applicative/BiFunctor.java @@ -5,7 +5,12 @@ import static com.jnape.palatable.lambda.functions.builtin.monadic.Identity.id; @FunctionalInterface -public interface BiFunctor { +public interface BiFunctor extends Functor { + + @Override + default BiFunctor fmap(MonadicFunction fn) { + return biMapR(fn); + } default BiFunctor biMapL(MonadicFunction fn) { return biMap(fn, id()); diff --git a/src/main/java/com/jnape/palatable/lambda/continuation/Continuation.java b/src/main/java/com/jnape/palatable/lambda/continuation/Continuation.java index 3c47077e0..bf9391909 100644 --- a/src/main/java/com/jnape/palatable/lambda/continuation/Continuation.java +++ b/src/main/java/com/jnape/palatable/lambda/continuation/Continuation.java @@ -1,14 +1,14 @@ package com.jnape.palatable.lambda.continuation; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.applicative.Functor; import com.jnape.palatable.lambda.functions.MonadicFunction; -import com.jnape.palatable.lambda.tuples.Tuple2; import java.util.Iterator; import java.util.Optional; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static com.jnape.palatable.lambda.continuation.Memo.memoize; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; @FunctionalInterface public interface Continuation extends Functor, Iterable { diff --git a/src/main/java/com/jnape/palatable/lambda/continuation/ContinuationIterator.java b/src/main/java/com/jnape/palatable/lambda/continuation/ContinuationIterator.java index b59b54ca6..6fae52eb7 100644 --- a/src/main/java/com/jnape/palatable/lambda/continuation/ContinuationIterator.java +++ b/src/main/java/com/jnape/palatable/lambda/continuation/ContinuationIterator.java @@ -1,6 +1,6 @@ package com.jnape.palatable.lambda.continuation; -import com.jnape.palatable.lambda.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import java.util.Iterator; import java.util.NoSuchElementException; diff --git a/src/main/java/com/jnape/palatable/lambda/continuation/IteratorWrappingContinuation.java b/src/main/java/com/jnape/palatable/lambda/continuation/IteratorWrappingContinuation.java index f6b83f2dd..a7c1b8114 100644 --- a/src/main/java/com/jnape/palatable/lambda/continuation/IteratorWrappingContinuation.java +++ b/src/main/java/com/jnape/palatable/lambda/continuation/IteratorWrappingContinuation.java @@ -1,12 +1,12 @@ package com.jnape.palatable.lambda.continuation; -import com.jnape.palatable.lambda.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import java.util.Iterator; import java.util.Optional; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static com.jnape.palatable.lambda.continuation.Memo.memoize; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; public final class IteratorWrappingContinuation implements Continuation { private final Memo>>> memoizedResult; diff --git a/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java b/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java index 720ce2ca8..acd8c8024 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java @@ -1,7 +1,7 @@ package com.jnape.palatable.lambda.functions; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.applicative.ProFunctor; -import com.jnape.palatable.lambda.tuples.Tuple2; @FunctionalInterface public interface DyadicFunction extends MonadicFunction>, ProFunctor { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/TriadicFunction.java b/src/main/java/com/jnape/palatable/lambda/functions/TriadicFunction.java index bafcdf730..4ca576955 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/TriadicFunction.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/TriadicFunction.java @@ -1,6 +1,6 @@ package com.jnape.palatable.lambda.functions; -import com.jnape.palatable.lambda.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; @FunctionalInterface public interface TriadicFunction extends DyadicFunction> { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java index ce76c57d5..73e8667f6 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java @@ -1,9 +1,9 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.CombinatorialIterator; -import com.jnape.palatable.lambda.tuples.Tuple2; public final class CartesianProduct implements DyadicFunction, Iterable, Iterable>> { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java index 2fcdc44d9..e782fce6f 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java @@ -6,7 +6,7 @@ import java.util.Optional; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; public final class Iterate implements DyadicFunction, A, Iterable> { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java index 654eb3971..d2ba8da72 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java @@ -1,9 +1,9 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.functions.DyadicFunction; -import com.jnape.palatable.lambda.tuples.Tuple2; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; public final class Tupler2 implements DyadicFunction> { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java index 0b82cbfed..f59fade86 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java @@ -1,9 +1,9 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.UnfoldingIterator; -import com.jnape.palatable.lambda.tuples.Tuple2; import java.util.Optional; diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Zip.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Zip.java index d260ab30e..93bb5890e 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Zip.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Zip.java @@ -1,8 +1,8 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; -import com.jnape.palatable.lambda.tuples.Tuple2; import static com.jnape.palatable.lambda.functions.builtin.triadic.ZipWith.zipWith; diff --git a/src/main/java/com/jnape/palatable/lambda/iterators/CombinatorialIterator.java b/src/main/java/com/jnape/palatable/lambda/iterators/CombinatorialIterator.java index c072fabc1..526cbd8c9 100644 --- a/src/main/java/com/jnape/palatable/lambda/iterators/CombinatorialIterator.java +++ b/src/main/java/com/jnape/palatable/lambda/iterators/CombinatorialIterator.java @@ -1,12 +1,12 @@ package com.jnape.palatable.lambda.iterators; -import com.jnape.palatable.lambda.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import java.util.ArrayList; import java.util.Iterator; import java.util.ListIterator; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; public class CombinatorialIterator extends ImmutableIterator> { private final Iterator asIterator; diff --git a/src/main/java/com/jnape/palatable/lambda/iterators/UnfoldingIterator.java b/src/main/java/com/jnape/palatable/lambda/iterators/UnfoldingIterator.java index 7939716e7..3b34b9ed7 100644 --- a/src/main/java/com/jnape/palatable/lambda/iterators/UnfoldingIterator.java +++ b/src/main/java/com/jnape/palatable/lambda/iterators/UnfoldingIterator.java @@ -1,7 +1,7 @@ package com.jnape.palatable.lambda.iterators; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.functions.MonadicFunction; -import com.jnape.palatable.lambda.tuples.Tuple2; import java.util.NoSuchElementException; import java.util.Optional; diff --git a/src/main/java/com/jnape/palatable/lambda/tuples/Tuple2.java b/src/main/java/com/jnape/palatable/lambda/tuples/Tuple2.java deleted file mode 100644 index bda5ade0a..000000000 --- a/src/main/java/com/jnape/palatable/lambda/tuples/Tuple2.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.jnape.palatable.lambda.tuples; - -import com.jnape.palatable.lambda.applicative.BiFunctor; -import com.jnape.palatable.lambda.functions.MonadicFunction; - -import static java.lang.String.format; - -public class Tuple2<_1, _2> implements BiFunctor<_1, _2> { - - public final _1 _1; - public final _2 _2; - - public Tuple2(_1 _1, _2 _2) { - this._1 = _1; - this._2 = _2; - } - - @Override - public Tuple2 biMap(MonadicFunction f1, - MonadicFunction f2) { - return tuple(f1.apply(_1), f2.apply(_2)); - } - - @Override - public Tuple2 biMapL(MonadicFunction fn) { - return (Tuple2) BiFunctor.super.biMapL(fn); - } - - @Override - public Tuple2<_1, C> biMapR(MonadicFunction fn) { - return (Tuple2<_1, C>) BiFunctor.super.biMapR(fn); - } - - @Override - public String toString() { - return format("(%s, %s)", _1, _2); - } - - @Override - public boolean equals(Object other) { - if (other instanceof Tuple2) { - Tuple2 that = (Tuple2) other; - return this._1.equals(that._1) && this._2.equals(that._2); - } - return false; - } - - @Override - public int hashCode() { - int result = _1.hashCode(); - return 31 * result + _2.hashCode(); - } - - public static <_1, _2> Tuple2<_1, _2> tuple(_1 _1, _2 _2) { - return new Tuple2<>(_1, _2); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/tuples/Tuple3.java b/src/main/java/com/jnape/palatable/lambda/tuples/Tuple3.java deleted file mode 100644 index 54c9d1ea3..000000000 --- a/src/main/java/com/jnape/palatable/lambda/tuples/Tuple3.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.jnape.palatable.lambda.tuples; - -import static java.lang.String.format; - -public class Tuple3<_1, _2, _3> extends Tuple2<_1, _2> { - - public final _3 _3; - - public Tuple3(_1 _1, _2 _2, _3 _3) { - super(_1, _2); - this._3 = _3; - } - - @Override - public String toString() { - return format("(%s, %s, %s)", _1, _2, _3); - } - - public static <_1, _2, _3> Tuple3<_1, _2, _3> tuple(_1 _1, _2 _2, _3 _3) { - return new Tuple3<>(_1, _2, _3); - } -} diff --git a/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java b/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java new file mode 100644 index 000000000..b54dfbbb9 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java @@ -0,0 +1,124 @@ +package com.jnape.palatable.lambda.adt; + +import com.jnape.palatable.lambda.functions.DyadicFunction; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.util.Optional; + +import static com.jnape.palatable.lambda.adt.Either.either; +import static com.jnape.palatable.lambda.adt.Either.left; +import static com.jnape.palatable.lambda.adt.Either.right; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class EitherTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void recoverFlattensByLiftingLeftBiasingTowardsRight() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.recover(l -> -1), is(-1)); + assertThat(right.recover(l -> -1), is(1)); + } + + @Test + public void orFlattensByReplacingLeftBiasingTowardsRight() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.or(-1), is(-1)); + assertThat(right.or(-1), is(1)); + } + + @Test + public void orThrowFlattensByLiftingAndThrowingLeftBiasingTowardsRight() { + Either left = left("foo"); + Either right = right(1); + + assertThat(right.orThrow(IllegalStateException::new), is(1)); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("foo"); + + left.orThrow(IllegalStateException::new); + } + + @Test + public void filterLiftsToTheRight() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.filter(x -> true, () -> "bar"), is(left)); + assertThat(left.filter(x -> false, () -> "bar"), is(left)); + assertThat(right.filter(x -> true, () -> "bar"), is(right)); + assertThat(right.filter(x -> false, () -> "bar"), is(left("bar"))); + } + + @Test + public void flatMapAdjunctivelyLifts() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.flatMap(l -> left(l + "bar"), r -> right(r + 1)), is(left("foobar"))); + assertThat(right.flatMap(l -> left(l + "bar"), r -> right(r + 1)), is(right(2))); + } + + @Test + public void mergeAdjunctivelyLiftsAndMergesBiasingTowardsLeft() { + Either left1 = left("foo"); + Either right1 = right(1); + + Either left2 = left("bar"); + Either right2 = right(2); + + DyadicFunction concat = String::concat; + DyadicFunction add = (r1, r2) -> r1 + r2; + + assertThat(left1.merge(concat, add, left2), is(left("foobar"))); + assertThat(left1.merge(concat, add, right2), is(left1)); + assertThat(right1.merge(concat, add, left2), is(left2)); + assertThat(right1.merge(concat, add, right1), is(right(2))); + } + + @Test + public void matchAdjunctivelyLiftsAndFlattens() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.match(l -> l + "bar", r -> r + 1), is("foobar")); + assertThat(right.match(l -> l + "bar", r -> r + 1), is(2)); + } + + @Test + public void eitherMapsOptionalToEither() { + Optional present = Optional.of("foo"); + Optional absent = Optional.empty(); + + assertThat(either(present, () -> -1), is(right("foo"))); + assertThat(either(absent, () -> -1), is(left(-1))); + } + + @Test + public void functorProperties() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.fmap(r -> r + 1), is(left)); + assertThat(right.fmap(r -> r + 1), is(right(2))); + } + + @Test + public void bifunctorProperties() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.biMap(l -> l + "bar", r -> r + 1), is(left("foobar"))); + assertThat(right.biMap(l -> l + "bar", r -> r + 1), is(right(2))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/tuples/Tuple2Test.java b/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple2Test.java similarity index 89% rename from src/test/java/com/jnape/palatable/lambda/tuples/Tuple2Test.java rename to src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple2Test.java index bc390178e..fec51a0de 100644 --- a/src/test/java/com/jnape/palatable/lambda/tuples/Tuple2Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple2Test.java @@ -1,8 +1,8 @@ -package com.jnape.palatable.lambda.tuples; +package com.jnape.palatable.lambda.adt.tuples; import org.junit.Test; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; @@ -12,14 +12,14 @@ public class Tuple2Test { @Test public void hasTwoSlots() { - Tuple2 stringIntTuple = new Tuple2<>("foo", 1); + Tuple2 stringIntTuple = tuple("foo", 1); assertThat(stringIntTuple._1, is("foo")); assertThat(stringIntTuple._2, is(1)); } @Test - public void mapsCovariantlyOverOverLeftSlot() { + public void mapsCovariantlyOverLeftSlot() { assertThat( tuple("foo", 1).biMapL(String::toUpperCase), is(tuple("FOO", 1)) diff --git a/src/test/java/com/jnape/palatable/lambda/tuples/Tuple3Test.java b/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple3Test.java similarity index 84% rename from src/test/java/com/jnape/palatable/lambda/tuples/Tuple3Test.java rename to src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple3Test.java index 72625e898..da07cd3cf 100644 --- a/src/test/java/com/jnape/palatable/lambda/tuples/Tuple3Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple3Test.java @@ -1,8 +1,8 @@ -package com.jnape.palatable.lambda.tuples; +package com.jnape.palatable.lambda.adt.tuples; import org.junit.Test; -import static com.jnape.palatable.lambda.tuples.Tuple3.tuple; +import static com.jnape.palatable.lambda.adt.tuples.Tuple3.tuple; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; diff --git a/src/test/java/com/jnape/palatable/lambda/applicative/BiFunctorTest.java b/src/test/java/com/jnape/palatable/lambda/applicative/BiFunctorTest.java index 9e23cd320..422ea280f 100644 --- a/src/test/java/com/jnape/palatable/lambda/applicative/BiFunctorTest.java +++ b/src/test/java/com/jnape/palatable/lambda/applicative/BiFunctorTest.java @@ -27,4 +27,15 @@ public void biMapRUsesIdentityForLeftBiMapFunction() { biFunctor.biMapR(String::valueOf); assertThat(leftInvocation.get(), is(id())); } + + @Test + public void functorProperties() { + AtomicReference leftInvocation = new AtomicReference<>(); + AtomicReference rightInvocation = new AtomicReference<>(); + BiFunctor biFunctor = new InvocationRecordingBiFunctor<>(leftInvocation, rightInvocation); + MonadicFunction fn = String::valueOf; + biFunctor.fmap(fn); + assertThat(leftInvocation.get(), is(id())); + assertThat(rightInvocation.get(), is(fn)); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationTest.java b/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationTest.java index d9b16a667..ce7bb4aa0 100644 --- a/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationTest.java +++ b/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationTest.java @@ -5,8 +5,8 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static com.jnape.palatable.lambda.continuation.Continuations.continuing; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static testsupport.matchers.IterableMatcher.iterates; diff --git a/src/test/java/com/jnape/palatable/lambda/functions/DyadicFunctionTest.java b/src/test/java/com/jnape/palatable/lambda/functions/DyadicFunctionTest.java index 945793895..dac5f6999 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/DyadicFunctionTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/DyadicFunctionTest.java @@ -2,7 +2,7 @@ import org.junit.Test; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; @@ -33,7 +33,7 @@ public void mapsContravariantlyOverSecondArgument() { @Test public void mapsCovariantlyOverReturnValue() { - assertThat(CHECK_LENGTH.diMapR(Object::toString).apply("123").apply(3), is("true")); + assertThat(CHECK_LENGTH.diMapR(Object::toString).apply("123").apply(3), is("true")); } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/functions/TriadicFunctionTest.java b/src/test/java/com/jnape/palatable/lambda/functions/TriadicFunctionTest.java index 0f5c2beb0..115e61d0e 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/TriadicFunctionTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/TriadicFunctionTest.java @@ -2,7 +2,7 @@ import org.junit.Test; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProductTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProductTest.java index baf960bac..af678b616 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProductTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProductTest.java @@ -1,7 +1,7 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.functions.MonadicFunction; -import com.jnape.palatable.lambda.tuples.Tuple2; import com.jnape.palatable.traitor.annotations.TestTraits; import com.jnape.palatable.traitor.runners.Traits; import org.junit.Test; @@ -11,8 +11,8 @@ import testsupport.traits.ImmutableIteration; import testsupport.traits.Laziness; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static com.jnape.palatable.lambda.functions.builtin.dyadic.CartesianProduct.cartesianProduct; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; import static java.util.Arrays.asList; import static org.junit.Assert.assertThat; import static testsupport.matchers.IterableMatcher.iterates; diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2Test.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2Test.java index 56c63ccf7..12cf85e2d 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2Test.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2Test.java @@ -2,7 +2,7 @@ import org.junit.Test; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/UnfoldrTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/UnfoldrTest.java index d14aca74c..458feda8c 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/UnfoldrTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/UnfoldrTest.java @@ -11,9 +11,9 @@ import java.util.Optional; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Take.take; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Unfoldr.unfoldr; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; import static org.junit.Assert.assertThat; import static testsupport.matchers.IterableMatcher.iterates; diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ZipTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ZipTest.java index aab481abc..5955a417f 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ZipTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ZipTest.java @@ -1,7 +1,7 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.functions.MonadicFunction; -import com.jnape.palatable.lambda.tuples.Tuple2; import com.jnape.palatable.traitor.annotations.TestTraits; import com.jnape.palatable.traitor.runners.Traits; import org.junit.Test; @@ -12,8 +12,8 @@ import java.util.Arrays; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Zip.zip; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; import static java.util.Arrays.asList; import static org.junit.Assert.assertThat; import static testsupport.matchers.IterableMatcher.iterates; diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ZipWithTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ZipWithTest.java index 4bfa7d147..d9b27f864 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ZipWithTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ZipWithTest.java @@ -1,8 +1,8 @@ package com.jnape.palatable.lambda.functions.builtin.triadic; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; -import com.jnape.palatable.lambda.tuples.Tuple2; import com.jnape.palatable.traitor.annotations.TestTraits; import com.jnape.palatable.traitor.runners.Traits; import org.junit.Test; @@ -11,9 +11,9 @@ import testsupport.traits.ImmutableIteration; import testsupport.traits.Laziness; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Zip.zip; import static com.jnape.palatable.lambda.functions.builtin.triadic.ZipWith.zipWith; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; import static java.util.Arrays.asList; import static org.junit.Assert.assertThat; import static testsupport.matchers.IterableMatcher.iterates; diff --git a/src/test/java/com/jnape/palatable/lambda/iterators/CombinatorialIteratorTest.java b/src/test/java/com/jnape/palatable/lambda/iterators/CombinatorialIteratorTest.java index 0b280a202..b7449b298 100644 --- a/src/test/java/com/jnape/palatable/lambda/iterators/CombinatorialIteratorTest.java +++ b/src/test/java/com/jnape/palatable/lambda/iterators/CombinatorialIteratorTest.java @@ -1,6 +1,6 @@ package com.jnape.palatable.lambda.iterators; -import com.jnape.palatable.lambda.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/src/test/java/com/jnape/palatable/lambda/iterators/UnfoldingIteratorTest.java b/src/test/java/com/jnape/palatable/lambda/iterators/UnfoldingIteratorTest.java index 097e4f938..0029e5e69 100644 --- a/src/test/java/com/jnape/palatable/lambda/iterators/UnfoldingIteratorTest.java +++ b/src/test/java/com/jnape/palatable/lambda/iterators/UnfoldingIteratorTest.java @@ -1,12 +1,12 @@ package com.jnape.palatable.lambda.iterators; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.functions.MonadicFunction; -import com.jnape.palatable.lambda.tuples.Tuple2; import org.junit.Test; import java.util.Optional; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; From 4fa3d9e3357923dd132d46115a1c02d0dc0c2389 Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 29 May 2016 16:40:38 -0600 Subject: [PATCH 07/18] Either#flatMap supports monadic form Either is no longer an interface --- .../jnape/palatable/lambda/adt/Either.java | 147 ++++++------------ .../palatable/lambda/adt/EitherTest.java | 19 ++- 2 files changed, 65 insertions(+), 101 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/adt/Either.java b/src/main/java/com/jnape/palatable/lambda/adt/Either.java index 475ebffed..e73da73ee 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/Either.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/Either.java @@ -9,109 +9,99 @@ import java.util.Optional; import java.util.function.Supplier; -public interface Either extends Functor, BiFunctor { +import static com.jnape.palatable.lambda.functions.builtin.monadic.Identity.id; - static Either either(Optional optional, Supplier leftFn) { - return optional.>map(Either::right) - .orElse(left(leftFn.get())); - } +public abstract class Either implements Functor, BiFunctor { - default R or(R defaultValue) { + public final R or(R defaultValue) { return recover(l -> defaultValue); } - R recover(MonadicFunction recoveryFn); + public final R recover(MonadicFunction recoveryFn) { + return match(recoveryFn, id()); + } + + public final R orThrow( + MonadicFunction throwableFn) throws E { + return match(l -> { + throw throwableFn.apply(l); + }, id()); + } - R orThrow(MonadicFunction throwableFn) throws E; + public final Either filter(MonadicFunction pred, + Supplier leftSupplier) { + return flatMap(r -> pred.apply(r) ? right(r) : left(leftSupplier.get())); + } - Either filter(MonadicFunction pred, - Supplier leftSupplier); + public final Either flatMap(MonadicFunction> rightFn) { + return flatMap(Either::left, rightFn); + } - Either flatMap(MonadicFunction> leftFn, - MonadicFunction> rightFn); + public final Either flatMap(MonadicFunction> leftFn, + MonadicFunction> rightFn) { + return match(leftFn, rightFn); + } - Either merge(DyadicFunction leftFn, - DyadicFunction rightFn, - Either other); + public final Either merge(DyadicFunction leftFn, + DyadicFunction rightFn, + Either other) { + return this.match( + l1 -> other.match(l2 -> left(leftFn.apply(l1, l2)), r -> left(l1)), + r1 -> other.match(Either::left, r2 -> right(rightFn.apply(r1, r2)))); + } - V match(MonadicFunction leftFn, - MonadicFunction rightFn); + public abstract V match(MonadicFunction leftFn, + MonadicFunction rightFn); @Override - default Either fmap(MonadicFunction fn) { + public final Either fmap(MonadicFunction fn) { return biMapR(fn); } @Override - default Either biMapL(MonadicFunction fn) { + @SuppressWarnings("unchecked") + public final Either biMapL(MonadicFunction fn) { return (Either) BiFunctor.super.biMapL(fn); } @Override - default Either biMapR(MonadicFunction fn) { + @SuppressWarnings("unchecked") + public final Either biMapR(MonadicFunction fn) { return (Either) BiFunctor.super.biMapR(fn); } @Override - Either biMap(MonadicFunction leftFn, - MonadicFunction rightFn); + public final Either biMap(MonadicFunction leftFn, + MonadicFunction rightFn) { + return match(l -> left(leftFn.apply(l)), r -> right(rightFn.apply(r))); + } + + public static Either fromOptional(Optional optional, Supplier leftFn) { + return optional.>map(Either::right) + .orElse(left(leftFn.get())); + } - static Either left(L l) { + public static Either left(L l) { return new Left<>(l); } - static Either right(R r) { + public static Either right(R r) { return new Right<>(r); } - final class Left implements Either { + private static final class Left extends Either { private final L l; private Left(L l) { this.l = l; } - @Override - public R recover(MonadicFunction recoveryFn) { - return recoveryFn.apply(l); - } - - @Override - public R orThrow(MonadicFunction throwableFn) throws E { - throw throwableFn.apply(l); - } - - @Override - public Either filter(MonadicFunction pred, Supplier leftSupplier) { - return this; - } - - @Override - public Either flatMap(MonadicFunction> leftFn, - MonadicFunction> rightFn) { - return leftFn.apply(l); - } - - @Override - public Either merge(DyadicFunction leftFn, - DyadicFunction rightFn, Either other) { - return other instanceof Left - ? left(leftFn.apply(l, ((Left) other).l)) - : this; - } - @Override public V match(MonadicFunction leftFn, MonadicFunction rightFn) { return leftFn.apply(l); } - @Override - public Either biMap(MonadicFunction leftFn, - MonadicFunction rightFn) { - return left(leftFn.apply(l)); - } - @Override public boolean equals(Object other) { return other instanceof Left && Objects.equals(l, ((Left) other).l); @@ -130,54 +120,19 @@ public String toString() { } } - final class Right implements Either { + private static final class Right extends Either { private final R r; private Right(R r) { this.r = r; } - @Override - public R recover(MonadicFunction recoveryFn) { - return r; - } - - @Override - public R orThrow(MonadicFunction throwableFn) throws E { - return r; - } - - @Override - public Either filter(MonadicFunction pred, Supplier leftSupplier) { - return pred.apply(r) ? this : left(leftSupplier.get()); - } - - @Override - public Either flatMap(MonadicFunction> leftFn, - MonadicFunction> rightFn) { - return rightFn.apply(r); - } - - @Override - public Either merge(DyadicFunction leftFn, - DyadicFunction rightFn, Either other) { - return other instanceof Right - ? right(rightFn.apply(r, ((Right) other).r)) - : other; - } - @Override public V match(MonadicFunction leftFn, MonadicFunction rightFn) { return rightFn.apply(r); } - @Override - public Either biMap(MonadicFunction leftFn, - MonadicFunction rightFn) { - return right(rightFn.apply(r)); - } - @Override public boolean equals(Object other) { return other instanceof Right && Objects.equals(r, ((Right) other).r); diff --git a/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java b/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java index b54dfbbb9..ba841dae0 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java @@ -7,7 +7,7 @@ import java.util.Optional; -import static com.jnape.palatable.lambda.adt.Either.either; +import static com.jnape.palatable.lambda.adt.Either.fromOptional; import static com.jnape.palatable.lambda.adt.Either.left; import static com.jnape.palatable.lambda.adt.Either.right; import static org.hamcrest.core.Is.is; @@ -60,6 +60,15 @@ public void filterLiftsToTheRight() { assertThat(right.filter(x -> false, () -> "bar"), is(left("bar"))); } + @Test + public void monadicFlatMapAppliesAndFlattensRight() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.flatMap(r -> right(r + 1)), is(left("foo"))); + assertThat(right.flatMap(r -> right(r + 1)), is(right(2))); + } + @Test public void flatMapAdjunctivelyLifts() { Either left = left("foo"); @@ -96,12 +105,12 @@ public void matchAdjunctivelyLiftsAndFlattens() { } @Test - public void eitherMapsOptionalToEither() { + public void fromOptionalMapsOptionalToEither() { Optional present = Optional.of("foo"); Optional absent = Optional.empty(); - assertThat(either(present, () -> -1), is(right("foo"))); - assertThat(either(absent, () -> -1), is(left(-1))); + assertThat(fromOptional(present, () -> -1), is(right("foo"))); + assertThat(fromOptional(absent, () -> -1), is(left(-1))); } @Test @@ -114,7 +123,7 @@ public void functorProperties() { } @Test - public void bifunctorProperties() { + public void biFunctorProperties() { Either left = left("foo"); Either right = right(1); From fd1be03addbd5ad9c7d01dc9b22e1fd697ec53f3 Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 29 May 2016 17:39:16 -0600 Subject: [PATCH 08/18] HList arrives --- .../com/jnape/palatable/lambda/adt/HList.java | 109 ++++++++++++++++++ .../jnape/palatable/lambda/adt/HListTest.java | 46 ++++++++ 2 files changed, 155 insertions(+) create mode 100644 src/main/java/com/jnape/palatable/lambda/adt/HList.java create mode 100644 src/test/java/com/jnape/palatable/lambda/adt/HListTest.java diff --git a/src/main/java/com/jnape/palatable/lambda/adt/HList.java b/src/main/java/com/jnape/palatable/lambda/adt/HList.java new file mode 100644 index 000000000..d688ea31b --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/HList.java @@ -0,0 +1,109 @@ +package com.jnape.palatable.lambda.adt; + +import com.jnape.palatable.lambda.applicative.Functor; +import com.jnape.palatable.lambda.functions.MonadicFunction; + +public abstract class HList> { + + private HList() { + } + + public abstract HCons> cons(NewHead newHead); + + public static HNil nil() { + return HNil.INSTANCE; + } + + public static > HCons cons(Head head, Tail tail) { + return new HCons<>(head, tail); + } + + public static HCons singleton(Head head) { + return cons(head, nil()); + } + + public static HCons> list(H1 h1, H2 h2) { + return singleton(h2).cons(h1); + } + + public static HCons>> list(H1 h1, H2 h2, H3 h3) { + return list(h2, h3).cons(h1); + } + + public static HCons>>> list(H1 h1, H2 h2, H3 h3, + H4 h4) { + return list(h2, h3, h4).cons(h1); + } + + public static HCons>>>> list(H1 h1, H2 h2, + H3 h3, H4 h4, + H5 h5) { + return list(h2, h3, h4, h5).cons(h1); + } + + public static final class HCons> extends HList implements Functor { + private final Head head; + private final Tail tail; + + private HCons(Head head, Tail tail) { + this.head = head; + this.tail = tail; + } + + public Head head() { + return head; + } + + public Tail tail() { + return tail; + } + + @Override + public HCons> cons(NewHead newHead) { + return new HCons<>(newHead, this); + } + + @Override + public HCons fmap(MonadicFunction fn) { + return new HCons<>(fn.apply(head), tail); + } + + @Override + public boolean equals(Object other) { + if (other instanceof HCons) { + HCons that = (HCons) other; + boolean sameHead = this.head.equals(that.head); + boolean sameTail = this.tail.equals(that.tail); + return sameHead && sameTail; + } + return false; + } + + @Override + public int hashCode() { + return 31 * head.hashCode() + tail.hashCode(); + } + + @Override + public String toString() { + return "HCons{" + + "head=" + head + + ", tail=" + tail + + '}'; + } + } + + public static final class HNil extends HList { + private static final HNil INSTANCE = new HNil(); + + @Override + public HCons cons(NewHead newHead) { + return new HCons<>(newHead, this); + } + + @Override + public String toString() { + return "HNil{}"; + } + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/adt/HListTest.java b/src/test/java/com/jnape/palatable/lambda/adt/HListTest.java new file mode 100644 index 000000000..d7d97501c --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/HListTest.java @@ -0,0 +1,46 @@ +package com.jnape.palatable.lambda.adt; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.HList.cons; +import static com.jnape.palatable.lambda.adt.HList.list; +import static com.jnape.palatable.lambda.adt.HList.nil; +import static com.jnape.palatable.lambda.adt.HList.singleton; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +public class HListTest { + + @Test + public void head() { + assertEquals("head", nil().cons("head").head()); + assertEquals("new head", nil().cons("old head").cons("new head").head()); + } + + @Test + public void tail() { + assertEquals(nil(), nil().cons("head").tail()); + assertEquals(nil().cons("old head"), nil().cons("old head").cons("new head").tail()); + } + + @Test + public void convenienceStaticFactoryMethods() { + assertEquals(nil().cons(1), cons(1, nil())); + + assertEquals(nil().cons(1), singleton(1)); + assertEquals(nil().cons('2').cons(1), list(1, '2')); + assertEquals(nil().cons("3").cons('2').cons(1), list(1, '2', "3")); + assertEquals(nil().cons(4.0).cons("3").cons('2').cons(1), list(1, '2', "3", 4.0)); + assertEquals(nil().cons(false).cons(4.0).cons("3").cons('2').cons(1), list(1, '2', "3", 4.0, false)); + } + + @Test + public void functorProperties() { + assertEquals(singleton("1"), singleton(1).fmap(Object::toString)); + } + + @Test + public void nilReusesInstance() { + assertSame(nil(), nil()); + } +} \ No newline at end of file From edcd00037eb6bdf2f7df32e591f74f4a1e74ddf1 Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 29 May 2016 17:56:57 -0600 Subject: [PATCH 09/18] Predicate can masquerade as a jufPredicate --- .../functions/specialized/Predicate.java | 22 +++++++- .../functions/specialized/PredicateTest.java | 54 +++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/specialized/PredicateTest.java diff --git a/src/main/java/com/jnape/palatable/lambda/functions/specialized/Predicate.java b/src/main/java/com/jnape/palatable/lambda/functions/specialized/Predicate.java index fbdce8810..5e62430b9 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/specialized/Predicate.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/specialized/Predicate.java @@ -2,5 +2,25 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; -public interface Predicate extends MonadicFunction { +public interface Predicate extends MonadicFunction, java.util.function.Predicate { + + @Override + default boolean test(A a) { + return apply(a); + } + + @Override + default Predicate and(java.util.function.Predicate other) { + return a -> apply(a) && other.test(a); + } + + @Override + default Predicate or(java.util.function.Predicate other) { + return a -> apply(a) || other.test(a); + } + + @Override + default Predicate negate() { + return a -> !apply(a); + } } diff --git a/src/test/java/com/jnape/palatable/lambda/functions/specialized/PredicateTest.java b/src/test/java/com/jnape/palatable/lambda/functions/specialized/PredicateTest.java new file mode 100644 index 000000000..b7c8be809 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/specialized/PredicateTest.java @@ -0,0 +1,54 @@ +package com.jnape.palatable.lambda.functions.specialized; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class PredicateTest { + + @Test + public void jufPredicateTest() { + Predicate notEmpty = s -> !(s == null || s.length() == 0); + + assertTrue(notEmpty.test("foo")); + assertFalse(notEmpty.test("")); + assertFalse(notEmpty.test(null)); + } + + @Test + public void jufPredicateAnd() { + Predicate notEmpty = s -> !(s == null || s.length() == 0); + Predicate lengthGt1 = s -> s.length() > 1; + + Predicate conjunction = notEmpty.and(lengthGt1); + + assertTrue(conjunction.test("fo")); + assertFalse(conjunction.test("f")); + assertFalse(conjunction.test("")); + assertFalse(conjunction.test(null)); + } + + @Test + public void jufPredicateOr() { + Predicate notEmpty = s -> !(s == null || s.length() == 0); + Predicate lengthGt1 = s -> s != null && s.length() > 1; + + Predicate disjunction = lengthGt1.or(notEmpty); + + assertTrue(disjunction.test("fo")); + assertTrue(disjunction.test("f")); + assertFalse(disjunction.test("")); + assertFalse(disjunction.test(null)); + } + + @Test + public void jufPredicateNegate() { + Predicate isTrue = x -> x; + + assertTrue(isTrue.test(true)); + assertFalse(isTrue.test(false)); + assertFalse(isTrue.negate().test(true)); + assertTrue(isTrue.negate().test(false)); + } +} \ No newline at end of file From c511054e6d721cb7f69e4a9dba2657a0567a2ecb Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 29 May 2016 23:55:12 -0500 Subject: [PATCH 10/18] HCons specializations for tuples of 1 to 5 elements to ease the pain of linearly expanding type signatures --- .../com/jnape/palatable/lambda/adt/HList.java | 76 ++++++++++++++++--- .../jnape/palatable/lambda/adt/HListTest.java | 6 +- 2 files changed, 66 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/adt/HList.java b/src/main/java/com/jnape/palatable/lambda/adt/HList.java index d688ea31b..1f07671e3 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/HList.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/HList.java @@ -18,30 +18,82 @@ public static HNil nil() { return new HCons<>(head, tail); } - public static HCons singleton(Head head) { - return cons(head, nil()); + public static HCons1 list(Head head) { + return new HCons1<>(head); } - public static HCons> list(H1 h1, H2 h2) { - return singleton(h2).cons(h1); + public static HCons2 list(H1 h1, H2 h2) { + return list(h2).cons(h1); } - public static HCons>> list(H1 h1, H2 h2, H3 h3) { + public static HCons3 list(H1 h1, H2 h2, H3 h3) { return list(h2, h3).cons(h1); } - public static HCons>>> list(H1 h1, H2 h2, H3 h3, - H4 h4) { + public static HCons4 list(H1 h1, H2 h2, H3 h3, H4 h4) { return list(h2, h3, h4).cons(h1); } - public static HCons>>>> list(H1 h1, H2 h2, - H3 h3, H4 h4, - H5 h5) { + public static HCons5 list(H1 h1, H2 h2, H3 h3, H4 h4, H5 h5) { return list(h2, h3, h4, h5).cons(h1); } - public static final class HCons> extends HList implements Functor { + public static final class HCons5 extends HCons> { + private HCons5(A a, HCons4 tail) { + super(a, tail); + } + + @Override + public HCons> cons(NewHead newHead) { + return new HCons<>(newHead, this); + } + } + + public static final class HCons4 extends HCons> { + private HCons4(A a, HCons3 tail) { + super(a, tail); + } + + @Override + public HCons5 cons(NewHead newHead) { + return new HCons5<>(newHead, this); + } + } + + public static final class HCons3 extends HCons> { + private HCons3(A a, HCons2 tail) { + super(a, tail); + } + + @Override + public HCons4 cons(NewHead newHead) { + return new HCons4<>(newHead, this); + } + } + + public static final class HCons2 extends HCons> { + public HCons2(A a, HCons1 tail) { + super(a, tail); + } + + @Override + public HCons3 cons(NewHead newHead) { + return new HCons3<>(newHead, this); + } + } + + public static final class HCons1 extends HCons { + private HCons1(Head head) { + super(head, nil()); + } + + @Override + public HCons2 cons(NewHead newHead) { + return new HCons2<>(newHead, this); + } + } + + public static class HCons> extends HList implements Functor { private final Head head; private final Tail tail; @@ -59,7 +111,7 @@ public Tail tail() { } @Override - public HCons> cons(NewHead newHead) { + public HCons> cons(NewHead newHead) { return new HCons<>(newHead, this); } diff --git a/src/test/java/com/jnape/palatable/lambda/adt/HListTest.java b/src/test/java/com/jnape/palatable/lambda/adt/HListTest.java index d7d97501c..8befc0a45 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/HListTest.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/HListTest.java @@ -5,7 +5,6 @@ import static com.jnape.palatable.lambda.adt.HList.cons; import static com.jnape.palatable.lambda.adt.HList.list; import static com.jnape.palatable.lambda.adt.HList.nil; -import static com.jnape.palatable.lambda.adt.HList.singleton; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; @@ -26,8 +25,7 @@ public void tail() { @Test public void convenienceStaticFactoryMethods() { assertEquals(nil().cons(1), cons(1, nil())); - - assertEquals(nil().cons(1), singleton(1)); + assertEquals(nil().cons(1), list(1)); assertEquals(nil().cons('2').cons(1), list(1, '2')); assertEquals(nil().cons("3").cons('2').cons(1), list(1, '2', "3")); assertEquals(nil().cons(4.0).cons("3").cons('2').cons(1), list(1, '2', "3", 4.0)); @@ -36,7 +34,7 @@ public void convenienceStaticFactoryMethods() { @Test public void functorProperties() { - assertEquals(singleton("1"), singleton(1).fmap(Object::toString)); + assertEquals(list("1"), list(1).fmap(Object::toString)); } @Test From afe888f1793a767b5aa30f86019e16ce107de32e Mon Sep 17 00:00:00 2001 From: jnape Date: Sat, 4 Jun 2016 15:12:05 -0500 Subject: [PATCH 11/18] Adding #trying and #forfeit to Either; adding Checked variants of MonadicFunction and Supplier --- .../jnape/palatable/lambda/adt/Either.java | 19 ++++++++ .../lambda/functions/specialized/Checked.java | 23 +++++++++ .../unchecked/CheckedMonadicFunction.java | 19 ++++++++ .../unchecked/CheckedSupplier.java | 19 ++++++++ .../palatable/lambda/adt/EitherTest.java | 48 +++++++++++++++---- .../functions/specialized/CheckedTest.java | 27 +++++++++++ 6 files changed, 145 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/specialized/Checked.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/specialized/unchecked/CheckedMonadicFunction.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/specialized/unchecked/CheckedSupplier.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/specialized/CheckedTest.java diff --git a/src/main/java/com/jnape/palatable/lambda/adt/Either.java b/src/main/java/com/jnape/palatable/lambda/adt/Either.java index e73da73ee..81e850d40 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/Either.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/Either.java @@ -4,6 +4,7 @@ import com.jnape.palatable.lambda.applicative.Functor; import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; +import com.jnape.palatable.lambda.functions.specialized.unchecked.CheckedSupplier; import java.util.Objects; import java.util.Optional; @@ -21,6 +22,10 @@ public final R recover(MonadicFunction recoveryFn) { return match(recoveryFn, id()); } + public final L forfeit(MonadicFunction forfeitFn) { + return match(id(), forfeitFn); + } + public final R orThrow( MonadicFunction throwableFn) throws E { return match(l -> { @@ -81,6 +86,20 @@ public static Either fromOptional(Optional optional, Supplier .orElse(left(leftFn.get())); } + @SuppressWarnings("unchecked") + public static Either trying(CheckedSupplier supplier, + MonadicFunction leftFn) { + try { + return right(supplier.get()); + } catch (Exception e) { + return left(leftFn.apply((E) e)); + } + } + + public static Either trying(CheckedSupplier supplier) { + return trying(supplier, id()); + } + public static Either left(L l) { return new Left<>(l); } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/specialized/Checked.java b/src/main/java/com/jnape/palatable/lambda/functions/specialized/Checked.java new file mode 100644 index 000000000..b16d66a24 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/specialized/Checked.java @@ -0,0 +1,23 @@ +package com.jnape.palatable.lambda.functions.specialized; + +import com.jnape.palatable.lambda.functions.MonadicFunction; +import com.jnape.palatable.lambda.functions.specialized.unchecked.CheckedMonadicFunction; +import com.jnape.palatable.lambda.functions.specialized.unchecked.CheckedSupplier; + +import java.util.function.Supplier; + +public class Checked { + + public static CheckedSupplier checked(Supplier supplier) { + return supplier::get; + } + + public static CheckedMonadicFunction checked(MonadicFunction fn) { + return fn::apply; + } + + @SuppressWarnings("unchecked") + public static RuntimeException throwChecked(Throwable ex) throws T { + throw (T) ex; + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/specialized/unchecked/CheckedMonadicFunction.java b/src/main/java/com/jnape/palatable/lambda/functions/specialized/unchecked/CheckedMonadicFunction.java new file mode 100644 index 000000000..7182f4542 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/specialized/unchecked/CheckedMonadicFunction.java @@ -0,0 +1,19 @@ +package com.jnape.palatable.lambda.functions.specialized.unchecked; + +import com.jnape.palatable.lambda.functions.MonadicFunction; + +import static com.jnape.palatable.lambda.functions.specialized.Checked.throwChecked; + +public interface CheckedMonadicFunction extends MonadicFunction { + + @Override + default B apply(A a) { + try { + return checkedApply(a); + } catch (Throwable t) { + throw throwChecked(t); + } + } + + B checkedApply(A a) throws Throwable; +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/specialized/unchecked/CheckedSupplier.java b/src/main/java/com/jnape/palatable/lambda/functions/specialized/unchecked/CheckedSupplier.java new file mode 100644 index 000000000..1ff288815 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/specialized/unchecked/CheckedSupplier.java @@ -0,0 +1,19 @@ +package com.jnape.palatable.lambda.functions.specialized.unchecked; + +import java.util.function.Supplier; + +import static com.jnape.palatable.lambda.functions.specialized.Checked.throwChecked; + +public interface CheckedSupplier extends Supplier { + + @Override + default T get() { + try { + return checkedGet(); + } catch (Throwable t) { + throw throwChecked(t); + } + } + + T checkedGet() throws E; +} diff --git a/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java b/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java index ba841dae0..2950ed5b0 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java @@ -11,6 +11,7 @@ import static com.jnape.palatable.lambda.adt.Either.left; import static com.jnape.palatable.lambda.adt.Either.right; import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; public class EitherTest { @@ -19,7 +20,7 @@ public class EitherTest { public ExpectedException thrown = ExpectedException.none(); @Test - public void recoverFlattensByLiftingLeftBiasingTowardsRight() { + public void recoverLiftsLeftAndFlattensRight() { Either left = left("foo"); Either right = right(1); @@ -28,7 +29,16 @@ public void recoverFlattensByLiftingLeftBiasingTowardsRight() { } @Test - public void orFlattensByReplacingLeftBiasingTowardsRight() { + public void forfeitLiftsRightAndFlattensLeft() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.forfeit(r -> "bar"), is("foo")); + assertThat(right.forfeit(r -> "bar"), is("bar")); + } + + @Test + public void orReplacesLeftAndFlattensRight() { Either left = left("foo"); Either right = right(1); @@ -37,7 +47,7 @@ public void orFlattensByReplacingLeftBiasingTowardsRight() { } @Test - public void orThrowFlattensByLiftingAndThrowingLeftBiasingTowardsRight() { + public void orThrowFlattensRightOrThrowsException() { Either left = left("foo"); Either right = right(1); @@ -50,7 +60,7 @@ public void orThrowFlattensByLiftingAndThrowingLeftBiasingTowardsRight() { } @Test - public void filterLiftsToTheRight() { + public void filterLiftsRight() { Either left = left("foo"); Either right = right(1); @@ -61,7 +71,7 @@ public void filterLiftsToTheRight() { } @Test - public void monadicFlatMapAppliesAndFlattensRight() { + public void monadicFlatMapLiftsRightAndFlattensBackToEither() { Either left = left("foo"); Either right = right(1); @@ -70,7 +80,7 @@ public void monadicFlatMapAppliesAndFlattensRight() { } @Test - public void flatMapAdjunctivelyLifts() { + public void dyadicFlatMapDuallyLiftsAndFlattensBackToEither() { Either left = left("foo"); Either right = right(1); @@ -79,7 +89,7 @@ public void flatMapAdjunctivelyLifts() { } @Test - public void mergeAdjunctivelyLiftsAndMergesBiasingTowardsLeft() { + public void mergeDuallyLiftsAndCombinesBiasingLeft() { Either left1 = left("foo"); Either right1 = right(1); @@ -96,7 +106,7 @@ public void mergeAdjunctivelyLiftsAndMergesBiasingTowardsLeft() { } @Test - public void matchAdjunctivelyLiftsAndFlattens() { + public void matchDuallyLiftsAndFlattens() { Either left = left("foo"); Either right = right(1); @@ -114,7 +124,7 @@ public void fromOptionalMapsOptionalToEither() { } @Test - public void functorProperties() { + public void functorialProperties() { Either left = left("foo"); Either right = right(1); @@ -123,11 +133,29 @@ public void functorProperties() { } @Test - public void biFunctorProperties() { + public void biFunctorialProperties() { Either left = left("foo"); Either right = right(1); assertThat(left.biMap(l -> l + "bar", r -> r + 1), is(left("foobar"))); assertThat(right.biMap(l -> l + "bar", r -> r + 1), is(right(2))); } + + @Test + public void monadicTryingLiftsCheckedSupplier() { + assertEquals(right(1), Either.trying(() -> 1)); + + Exception checkedException = new Exception("expected"); + assertEquals(left(checkedException), Either.trying(() -> { + throw checkedException; + })); + } + + @Test + public void dyadicTryingLiftsCheckedSupplierMappingAnyThrownExceptions() { + assertEquals(right(1), Either.trying(() -> 1, Throwable::getMessage)); + assertEquals(left("expected"), Either.trying(() -> { + throw new Exception("expected"); + }, Throwable::getMessage)); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/functions/specialized/CheckedTest.java b/src/test/java/com/jnape/palatable/lambda/functions/specialized/CheckedTest.java new file mode 100644 index 000000000..df6f9c388 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/specialized/CheckedTest.java @@ -0,0 +1,27 @@ +package com.jnape.palatable.lambda.functions.specialized; + +import com.jnape.palatable.lambda.functions.specialized.unchecked.CheckedMonadicFunction; +import com.jnape.palatable.lambda.functions.specialized.unchecked.CheckedSupplier; +import org.junit.Test; + +import static com.jnape.palatable.lambda.functions.specialized.Checked.checked; + + +public class CheckedTest { + + @Test(expected = IllegalStateException.class) + public void checkedSupplier() { + CheckedSupplier supplier = checked(() -> { + throw new IllegalStateException("expected"); + }); + supplier.get(); + } + + @Test(expected = IllegalStateException.class) + public void uncheckedMonadicFunction() { + CheckedMonadicFunction fn = checked(x -> { + throw new IllegalStateException("expected"); + }); + fn.apply("foo"); + } +} \ No newline at end of file From 32952a89838d7cc84f94a82135a3ba6a7c3bb3a7 Mon Sep 17 00:00:00 2001 From: jnape Date: Sat, 4 Jun 2016 15:21:04 -0500 Subject: [PATCH 12/18] Fixing warnings and adding missing Tuple3 tests --- .../palatable/lambda/adt/tuples/Tuple3.java | 17 +++++++---- .../lambda/functions/DyadicFunction.java | 2 ++ .../lambda/adt/tuples/Tuple3Test.java | 29 +++++++++++++++++++ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple3.java b/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple3.java index 8a2d7c886..bcb5954b0 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple3.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple3.java @@ -16,33 +16,38 @@ public Tuple3(_1 _1, _2 _2, _3 _3) { } @Override + @SuppressWarnings("unchecked") public <_2A> Tuple3<_1, _2A, _3> fmap(MonadicFunction fn) { return (Tuple3<_1, _2A, _3>) super.fmap(fn); } @Override + @SuppressWarnings("unchecked") public <_1A, _2A> Tuple3<_1A, _2A, _3> biMap(MonadicFunction f1, MonadicFunction f2) { return (Tuple3<_1A, _2A, _3>) super.biMap(f1, f2); } @Override + @SuppressWarnings("unchecked") public <_1A> Tuple3<_1A, _2, _3> biMapL(MonadicFunction fn) { return (Tuple3<_1A, _2, _3>) super.biMapL(fn); } @Override + @SuppressWarnings("unchecked") public <_2A> Tuple3<_1, _2A, _3> biMapR(MonadicFunction fn) { return (Tuple3<_1, _2A, _3>) super.biMapR(fn); } @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; - Tuple3 tuple3 = (Tuple3) o; - return Objects.equals(_3, tuple3._3); + public boolean equals(Object other) { + if (other instanceof Tuple3) { + Tuple3 that = (Tuple3) other; + return super.equals(other) + && Objects.equals(this._3, that._3); + } + return false; } @Override diff --git a/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java b/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java index acd8c8024..b7a669874 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java @@ -14,11 +14,13 @@ default MonadicFunction apply(A a) { } @Override + @SuppressWarnings("unchecked") default DyadicFunction diMapL(MonadicFunction fn) { return (DyadicFunction) ProFunctor.super.diMapL(fn); } @Override + @SuppressWarnings("unchecked") default DyadicFunction diMapR(MonadicFunction fn) { return (DyadicFunction) ProFunctor.super.diMapR(fn); } diff --git a/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple3Test.java b/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple3Test.java index da07cd3cf..e1cfc07bf 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple3Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple3Test.java @@ -4,6 +4,8 @@ import static com.jnape.palatable.lambda.adt.tuples.Tuple3.tuple; import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertThat; public class Tuple3Test { @@ -17,6 +19,33 @@ public void hasThreeSlots() { assertThat(integerStringCharacterTuple._3, is('3')); } + @Test + public void usesValueBasedEquality() { + Tuple3 same = tuple("a", "b", 1); + Tuple3 alsoSame = tuple("a", "b", 1); + Tuple3 different = tuple("b", "b", 2); + + assertEquals(same, alsoSame); + assertEquals(alsoSame, same); + + assertNotEquals(same, different); + assertNotEquals(alsoSame, different); + assertNotEquals(different, same); + assertNotEquals(different, alsoSame); + + assertNotEquals(same, new Object()); + } + + @Test + public void hashesCorrectlyForEqualTuples() { + assertEquals(tuple("foo", "bar", 1).hashCode(), tuple("foo", "bar", 1).hashCode()); + } + + @Test + public void hashesUsingReasonableDistribution() { + assertNotEquals(tuple("foo", "bar", 1).hashCode(), tuple("bar", "baz", 2).hashCode()); + } + @Test public void toStringIsReasonable() { assertThat(tuple("foo", "bar", "baz").toString(), is("(foo, bar, baz)")); From 80c69e41660669ec39c3049a14056d07ad5db683 Mon Sep 17 00:00:00 2001 From: jnape Date: Sat, 4 Jun 2016 15:58:56 -0500 Subject: [PATCH 13/18] Adding more JavaDocs, and missing private constructors / partial overloads for dyadic functions --- .../lambda/functions/builtin/dyadic/All.java | 13 +++++++++++- .../lambda/functions/builtin/dyadic/Any.java | 13 +++++++++++- .../builtin/dyadic/CartesianProduct.java | 21 +++++++++++++++++-- .../lambda/functions/builtin/dyadic/Drop.java | 12 +++++++++++ .../functions/builtin/dyadic/DropWhile.java | 17 +++++++++++++-- .../functions/builtin/dyadic/Filter.java | 13 +++++++++++- .../functions/builtin/dyadic/InGroupsOf.java | 10 +++++++++ .../functions/builtin/dyadic/Iterate.java | 12 ++++++++++- .../lambda/functions/builtin/dyadic/Map.java | 12 ++++++++++- .../functions/builtin/dyadic/Partial2.java | 3 +++ .../functions/builtin/dyadic/Partial3.java | 3 +++ .../functions/builtin/dyadic/ReduceLeft.java | 5 ++++- .../functions/builtin/dyadic/ReduceRight.java | 5 ++++- .../lambda/functions/builtin/dyadic/Take.java | 12 +++++++++++ .../functions/builtin/dyadic/TakeWhile.java | 14 ++++++++++++- .../functions/builtin/dyadic/Tupler2.java | 12 +++++++++++ .../functions/builtin/dyadic/Unfoldr.java | 3 +++ .../lambda/functions/builtin/dyadic/Zip.java | 6 +++++- .../functions/builtin/dyadic/Tupler2Test.java | 4 ++-- .../functions/builtin/dyadic/UnfoldrTest.java | 2 +- 20 files changed, 176 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/All.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/All.java index d722ef60e..9b82a21e1 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/All.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/All.java @@ -3,8 +3,19 @@ import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; +/** + * Eagerly apply a predicate to each element in an Iterable, returning true if every element + * satisfies the predicate, and false otherwise. This method short-circuits on the first false + * evaluation. + * + * @param The input Iterable element type + * @see Any + */ public final class All implements DyadicFunction, Iterable, Boolean> { + private All() { + } + @Override public final Boolean apply(MonadicFunction predicate, Iterable as) { for (A a : as) @@ -23,6 +34,6 @@ public static MonadicFunction, Boolean> all(MonadicFunction boolean all(MonadicFunction predicate, Iterable as) { - return all(predicate).apply(as); + return All.all(predicate).apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Any.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Any.java index 87251cb1c..c3ecde1f7 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Any.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Any.java @@ -3,8 +3,19 @@ import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; +/** + * Eagerly apply a predicate to each element in an Iterable, returning true if any element + * satisfies the predicate, and false otherwise. This method short-circuits on the first true + * evaluation. + * + * @param The input Iterable element type + * @see All + */ public final class Any implements DyadicFunction, Iterable, Boolean> { + private Any() { + } + @Override public final Boolean apply(MonadicFunction predicate, Iterable as) { for (A a : as) @@ -23,6 +34,6 @@ public static MonadicFunction, Boolean> any(MonadicFunction Boolean any(MonadicFunction predicate, Iterable as) { - return any(predicate).apply(as); + return Any.any(predicate).apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java index 73e8667f6..bf93b7948 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java @@ -5,10 +5,27 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.CombinatorialIterator; +/** + * Lazily compute the cartesian product of an Iterable<A> and Iterable<B>, + * returning an Iterable<Tuple2<A, B>>, the products as tuples of + * multiplicand As and multiplier Bs. + *

+ * Note that this algorithm exhaustively pairs all elements from Iterable<B> to the first element of + * Iterable<A> before advancing to the next element of Iterable<A>, so if + * Iterable<B> is infinite, only one element from Iterable<A> will ever be + * paired. + * + * @param The multiplicand Iterable element type + * @param The multiplier Iterable element type + * @see Zip + */ public final class CartesianProduct implements DyadicFunction, Iterable, Iterable>> { + private CartesianProduct() { + } + @Override - public final Iterable> apply(final Iterable as, final Iterable bs) { + public final Iterable> apply(Iterable as, Iterable bs) { return () -> new CombinatorialIterator<>(as.iterator(), bs.iterator()); } @@ -20,7 +37,7 @@ public static MonadicFunction, Iterable>> cartes return CartesianProduct.cartesianProduct().apply(as); } - public static Iterable> cartesianProduct(final Iterable as, final Iterable bs) { + public static Iterable> cartesianProduct(Iterable as, Iterable bs) { return CartesianProduct.cartesianProduct(as).apply(bs); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Drop.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Drop.java index ffc02e099..146d9b396 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Drop.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Drop.java @@ -4,8 +4,20 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.DroppingIterator; +/** + * Lazily skip the first n elements from an Iterable by returning an Iterable + * that begins iteration after the nth element. If n is greater than or equal to the length of + * the Iterable, an empty Iterable is returned. + * + * @param The Iterable element type + * @see DropWhile + * @see Take + */ public final class Drop implements DyadicFunction, Iterable> { + private Drop() { + } + @Override public final Iterable apply(final Integer n, final Iterable as) { return () -> new DroppingIterator<>(n, as.iterator()); diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhile.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhile.java index 1b12aae0e..ef5d28dbe 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhile.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhile.java @@ -4,7 +4,21 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.PredicatedDroppingIterator; +/** + * Lazily limit the Iterable by skipping the first contiguous group of elements that satisfy the predicate, + * beginning iteration at the first element for which the predicate evaluates to false. + * + * @param The Iterable element type + * @see Drop + * @see Filter + * @see TakeWhile + */ + public final class DropWhile implements DyadicFunction, Iterable, Iterable> { + + private DropWhile() { + } + @Override public final Iterable apply(final MonadicFunction predicate, final Iterable as) { return () -> new PredicatedDroppingIterator<>(predicate, as.iterator()); @@ -20,7 +34,6 @@ public static MonadicFunction, Iterable> dropWhile( } public static Iterable dropWhile(MonadicFunction predicate, Iterable as) { - return dropWhile(predicate).apply(as); + return DropWhile.dropWhile(predicate).apply(as); } - } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Filter.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Filter.java index e40a63784..bc965e7f3 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Filter.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Filter.java @@ -4,8 +4,19 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.FilteringIterator; +/** + * Lazily apply a predicate to each element in an Iterable, returning an Iterable of just the + * elements for which the predicate evaluated to true. + * + * @param A type contravariant to the input Iterable element type + * @see TakeWhile + * @see DropWhile + */ public final class Filter implements DyadicFunction, Iterable, Iterable> { + private Filter() { + } + @Override public final Iterable apply(final MonadicFunction predicate, final Iterable as) { return () -> new FilteringIterator<>(predicate, as.iterator()); @@ -21,6 +32,6 @@ public static MonadicFunction, Iterable> filter( } public static Iterable filter(final MonadicFunction predicate, final Iterable as) { - return filter(predicate).apply(as); + return Filter.filter(predicate).apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/InGroupsOf.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/InGroupsOf.java index 6edb9d831..356b29fd9 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/InGroupsOf.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/InGroupsOf.java @@ -4,8 +4,18 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.GroupingIterator; +/** + * Lazily group the Iterable by returning an Iterable of smaller Iterables of + * size k. Note that groups are not padded; that is, if k >= n, where n + * is the number of remaining elements, the final Iterable will have only n elements. + * + * @param The Iterable element type + */ public final class InGroupsOf implements DyadicFunction, Iterable>> { + private InGroupsOf() { + } + @Override public final Iterable> apply(final Integer k, final Iterable as) { return () -> new GroupingIterator<>(k, as.iterator()); diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java index e782fce6f..8d2d88219 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java @@ -8,8 +8,18 @@ import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; +/** + * Lazily generate an infinite Iterable from the successive applications of the function first to the + * initial seed value, then to the result, and so on; i.e., the result of iterate(x -> x + 1, 0) would + * produce an infinite Iterable over the elements 0, 1, 2, 3, ... and so on. + * + * @param The Iterable element type + */ public final class Iterate implements DyadicFunction, A, Iterable> { + private Iterate() { + } + @Override public final Iterable apply(final MonadicFunction fn, final A seed) { return () -> new UnfoldingIterator<>(a -> Optional.of(tuple(a, fn.apply(a))), seed); @@ -24,6 +34,6 @@ public static MonadicFunction> iterate(MonadicFunction Iterable iterate(MonadicFunction fn, A seed) { - return iterate(fn).apply(seed); + return Iterate.iterate(fn).apply(seed); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Map.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Map.java index c64b9202a..2a6797d37 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Map.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Map.java @@ -4,8 +4,18 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.MappingIterator; +/** + * Lazily apply a function to each element in an Iterable, producing an Iterable of the mapped + * results. + * + * @param A type contravariant to the input Iterable element type + * @param A type covariant to the output Iterable element type + */ public final class Map implements DyadicFunction, Iterable, Iterable> { + private Map() { + } + @Override public final Iterable apply(final MonadicFunction function, final Iterable as) { return () -> new MappingIterator<>(function, as.iterator()); @@ -21,6 +31,6 @@ public static MonadicFunction, Iterable> map( } public static Iterable map(MonadicFunction function, Iterable as) { - return map(function).apply(as); + return Map.map(function).apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial2.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial2.java index 6ba8c94a4..4f198ff56 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial2.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial2.java @@ -5,6 +5,9 @@ public final class Partial2 implements DyadicFunction, A, MonadicFunction> { + private Partial2() { + } + @Override public MonadicFunction apply(DyadicFunction function, A a) { return function.apply(a); diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial3.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial3.java index 9bdf0c71c..db455b84e 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial3.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial3.java @@ -6,6 +6,9 @@ public final class Partial3 implements DyadicFunction, A, DyadicFunction> { + private Partial3() { + } + @Override public DyadicFunction apply(TriadicFunction function, A a) { return function.apply(a); diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeft.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeft.java index bdebfedde..cda2c7be2 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeft.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeft.java @@ -10,6 +10,9 @@ public final class ReduceLeft implements DyadicFunction, Iterable, A> { + private ReduceLeft() { + } + @Override public final A apply(DyadicFunction function, Iterable as) { final Iterator iterator = as.iterator(); @@ -30,6 +33,6 @@ public static MonadicFunction, A> reduceLeft( } public static A reduceLeft(DyadicFunction function, Iterable as) { - return reduceLeft(function).apply(as); + return ReduceLeft.reduceLeft(function).apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceRight.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceRight.java index 990f5078d..9c20ed2f5 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceRight.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceRight.java @@ -8,6 +8,9 @@ public final class ReduceRight implements DyadicFunction, Iterable, A> { + private ReduceRight() { + } + @Override public final A apply(DyadicFunction function, Iterable as) { return reduceLeft(function.flip(), reverse(as)); @@ -23,6 +26,6 @@ public static MonadicFunction, A> reduceRight( } public static A reduceRight(DyadicFunction function, Iterable as) { - return reduceRight(function).apply(as); + return ReduceRight.reduceRight(function).apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Take.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Take.java index aab3a81bb..defc6c71b 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Take.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Take.java @@ -4,8 +4,20 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.TakingIterator; +/** + * Lazily limit the Iterable to n elements by returning an Iterable that stops + * iteration after the nth element, or the last element of the Iterable, whichever comes + * first. + * + * @param The Iterable element type + * @see TakeWhile + * @see Drop + */ public final class Take implements DyadicFunction, Iterable> { + private Take() { + } + @Override public final Iterable apply(final Integer n, final Iterable as) { return () -> new TakingIterator<>(n, as.iterator()); diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhile.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhile.java index a4ea0b5c3..c7db67e7b 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhile.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhile.java @@ -4,8 +4,20 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.PredicatedTakingIterator; +/** + * Lazily limit the Iterable to the first group of contiguous elements that satisfy the predicate by + * iterating up to, but not including, the first element for which the predicate evaluates to false. + * + * @param The Iterable element type + * @see Take + * @see Filter + * @see DropWhile + */ public final class TakeWhile implements DyadicFunction, Iterable, Iterable> { + private TakeWhile() { + } + @Override public final Iterable apply(final MonadicFunction predicate, final Iterable as) { return () -> new PredicatedTakingIterator<>(predicate, as.iterator()); @@ -21,6 +33,6 @@ public static MonadicFunction, Iterable> takeWhile( } public static Iterable takeWhile(MonadicFunction predicate, Iterable as) { - return takeWhile(predicate).apply(as); + return TakeWhile.takeWhile(predicate).apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java index d2ba8da72..38a56db23 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java @@ -2,11 +2,15 @@ import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.functions.DyadicFunction; +import com.jnape.palatable.lambda.functions.MonadicFunction; import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; public final class Tupler2 implements DyadicFunction> { + private Tupler2() { + } + @Override public final Tuple2 apply(A a, B b) { return tuple(a, b); @@ -15,4 +19,12 @@ public final Tuple2 apply(A a, B b) { public static Tupler2 tupler() { return new Tupler2<>(); } + + public static MonadicFunction> tupler(A a) { + return Tupler2.tupler().apply(a); + } + + public static Tuple2 tupler(A a, B b) { + return Tupler2.tupler(a).apply(b); + } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java index f59fade86..502fdd1b8 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java @@ -9,6 +9,9 @@ public final class Unfoldr implements DyadicFunction>>, B, Iterable> { + private Unfoldr() { + } + @Override public Iterable apply(MonadicFunction>> function, B b) { return () -> new UnfoldingIterator<>(function, b); diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Zip.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Zip.java index 93bb5890e..3274df152 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Zip.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Zip.java @@ -4,13 +4,17 @@ import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; +import static com.jnape.palatable.lambda.functions.builtin.dyadic.Tupler2.tupler; import static com.jnape.palatable.lambda.functions.builtin.triadic.ZipWith.zipWith; public final class Zip implements DyadicFunction, Iterable, Iterable>> { + private Zip() { + } + @Override public final Iterable> apply(final Iterable as, final Iterable bs) { - return zipWith(Tupler2.tupler(), as, bs); + return zipWith(tupler(), as, bs); } public static Zip zip() { diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2Test.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2Test.java index 12cf85e2d..be0eb7a28 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2Test.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2Test.java @@ -3,6 +3,7 @@ import org.junit.Test; import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.functions.builtin.dyadic.Tupler2.tupler; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; @@ -10,7 +11,6 @@ public class Tupler2Test { @Test public void createsTupleOfTwoThings() { - Tupler2 tupler = new Tupler2<>(); - assertThat(tupler.apply("a", 1), is(tuple("a", 1))); + assertThat(tupler("a", 1), is(tuple("a", 1))); } } diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/UnfoldrTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/UnfoldrTest.java index 458feda8c..7a9722d73 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/UnfoldrTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/UnfoldrTest.java @@ -22,7 +22,7 @@ public class UnfoldrTest { @TestTraits({Laziness.class, InfiniteIteration.class, ImmutableIteration.class}) public MonadicFunction createTestSubject() { - return new Unfoldr().apply(x -> Optional.of(tuple(x, x))); + return unfoldr(x -> Optional.of(tuple(x, x))); } @Test From 7f4a9e3f6d41f7c0005bc2ba4781646bbb4d8b7d Mon Sep 17 00:00:00 2001 From: jnape Date: Mon, 6 Jun 2016 15:41:45 -0500 Subject: [PATCH 14/18] Fixing maven's javadoc nitpicks --- .../lambda/functions/builtin/dyadic/CartesianProduct.java | 2 +- .../palatable/lambda/functions/builtin/dyadic/InGroupsOf.java | 2 +- .../palatable/lambda/functions/builtin/dyadic/Iterate.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java index bf93b7948..6230a3fa6 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java @@ -9,7 +9,7 @@ * Lazily compute the cartesian product of an Iterable<A> and Iterable<B>, * returning an Iterable<Tuple2<A, B>>, the products as tuples of * multiplicand As and multiplier Bs. - *

+ *

* Note that this algorithm exhaustively pairs all elements from Iterable<B> to the first element of * Iterable<A> before advancing to the next element of Iterable<A>, so if * Iterable<B> is infinite, only one element from Iterable<A> will ever be diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/InGroupsOf.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/InGroupsOf.java index 356b29fd9..c1a0fe5ea 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/InGroupsOf.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/InGroupsOf.java @@ -6,7 +6,7 @@ /** * Lazily group the Iterable by returning an Iterable of smaller Iterables of - * size k. Note that groups are not padded; that is, if k >= n, where n + * size k. Note that groups are not padded; that is, if k >= n, where n * is the number of remaining elements, the final Iterable will have only n elements. * * @param The Iterable element type diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java index 8d2d88219..79d0716a5 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java @@ -10,7 +10,7 @@ /** * Lazily generate an infinite Iterable from the successive applications of the function first to the - * initial seed value, then to the result, and so on; i.e., the result of iterate(x -> x + 1, 0) would + * initial seed value, then to the result, and so on; i.e., the result of iterate(x -> x + 1, 0) would * produce an infinite Iterable over the elements 0, 1, 2, 3, ... and so on. * * @param The Iterable element type From e058e8a8f1ec5b4c0d49902c11709e9ed8e90d84 Mon Sep 17 00:00:00 2001 From: jnape Date: Mon, 6 Jun 2016 16:40:00 -0500 Subject: [PATCH 15/18] adding distributionManagement to pom --- pom.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pom.xml b/pom.xml index 8d10ef325..ad3aaba01 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,17 @@ git@github.com:palatable/lambda.git + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + jnape From 8aa7f5728d68cb87e16d031404b75d4795ccf074 Mon Sep 17 00:00:00 2001 From: jnape Date: Sat, 18 Jun 2016 19:35:47 -0500 Subject: [PATCH 16/18] Breaking BiFunctor inheritance on Functor; MonadicFunction now extends j.u.f.Function --- .../palatable/lambda/adt/tuples/Tuple2.java | 7 ++-- .../lambda/applicative/BiFunctor.java | 7 +--- .../lambda/functions/MonadicFunction.java | 4 ++- .../jnape/palatable/lambda/adt/HListTest.java | 2 +- .../lambda/adt/tuples/Tuple2Test.java | 36 +++++++------------ .../lambda/applicative/BiFunctorTest.java | 11 ------ 6 files changed, 22 insertions(+), 45 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple2.java b/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple2.java index eb939ff8e..6df82af8b 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple2.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple2.java @@ -1,13 +1,14 @@ package com.jnape.palatable.lambda.adt.tuples; import com.jnape.palatable.lambda.applicative.BiFunctor; +import com.jnape.palatable.lambda.applicative.Functor; import com.jnape.palatable.lambda.functions.MonadicFunction; import java.util.Objects; import static java.lang.String.format; -public class Tuple2<_1, _2> implements BiFunctor<_1, _2> { +public class Tuple2<_1, _2> implements Functor<_2>, BiFunctor<_1, _2> { public final _1 _1; public final _2 _2; @@ -19,7 +20,7 @@ public Tuple2(_1 _1, _2 _2) { @Override public <_2A> Tuple2<_1, _2A> fmap(MonadicFunction fn) { - return (Tuple2<_1, _2A>) BiFunctor.super.fmap(fn); + return biMapR(fn); } @Override @@ -29,11 +30,13 @@ public <_1A, _2A> Tuple2<_1A, _2A> biMap(MonadicFunction Tuple2<_1A, _2> biMapL(MonadicFunction fn) { return (Tuple2<_1A, _2>) BiFunctor.super.biMapL(fn); } @Override + @SuppressWarnings("unchecked") public <_2A> Tuple2<_1, _2A> biMapR(MonadicFunction fn) { return (Tuple2<_1, _2A>) BiFunctor.super.biMapR(fn); } diff --git a/src/main/java/com/jnape/palatable/lambda/applicative/BiFunctor.java b/src/main/java/com/jnape/palatable/lambda/applicative/BiFunctor.java index c3f2650c4..1a63dd236 100644 --- a/src/main/java/com/jnape/palatable/lambda/applicative/BiFunctor.java +++ b/src/main/java/com/jnape/palatable/lambda/applicative/BiFunctor.java @@ -5,12 +5,7 @@ import static com.jnape.palatable.lambda.functions.builtin.monadic.Identity.id; @FunctionalInterface -public interface BiFunctor extends Functor { - - @Override - default BiFunctor fmap(MonadicFunction fn) { - return biMapR(fn); - } +public interface BiFunctor { default BiFunctor biMapL(MonadicFunction fn) { return biMap(fn, id()); diff --git a/src/main/java/com/jnape/palatable/lambda/functions/MonadicFunction.java b/src/main/java/com/jnape/palatable/lambda/functions/MonadicFunction.java index 2668dd0fd..f6c142d63 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/MonadicFunction.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/MonadicFunction.java @@ -2,8 +2,10 @@ import com.jnape.palatable.lambda.applicative.Functor; +import java.util.function.Function; + @FunctionalInterface -public interface MonadicFunction extends Functor { +public interface MonadicFunction extends Functor, Function { B apply(A a); diff --git a/src/test/java/com/jnape/palatable/lambda/adt/HListTest.java b/src/test/java/com/jnape/palatable/lambda/adt/HListTest.java index 8befc0a45..bdac78d81 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/HListTest.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/HListTest.java @@ -33,7 +33,7 @@ public void convenienceStaticFactoryMethods() { } @Test - public void functorProperties() { + public void functorialProperties() { assertEquals(list("1"), list(1).fmap(Object::toString)); } diff --git a/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple2Test.java b/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple2Test.java index fec51a0de..4a183cd5e 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple2Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple2Test.java @@ -18,30 +18,6 @@ public void hasTwoSlots() { assertThat(stringIntTuple._2, is(1)); } - @Test - public void mapsCovariantlyOverLeftSlot() { - assertThat( - tuple("foo", 1).biMapL(String::toUpperCase), - is(tuple("FOO", 1)) - ); - } - - @Test - public void mapsCovariantlyOverRightSlot() { - assertThat( - tuple("foo", 1).biMapR(String::valueOf), - is(tuple("foo", "1")) - ); - } - - @Test - public void mapsOverBothSlotsInSingleTransformation() { - assertThat( - tuple("foo", 1).biMap(String::toUpperCase, i -> i + 1), - is(tuple("FOO", 2)) - ); - } - @Test public void toStringIsReasonable() { assertThat(tuple("1", "2").toString(), is("(1, 2)")); @@ -73,4 +49,16 @@ public void hashesCorrectlyForEqualTuples() { public void hashesUsingReasonableDistribution() { assertNotEquals(tuple("foo", 1).hashCode(), tuple("bar", 2).hashCode()); } + + @Test + public void functorialProperties() { + assertThat(tuple("foo", 1).fmap(String::valueOf), is(tuple("foo", "1"))); + } + + @Test + public void biFunctorialProperties() { + assertThat(tuple("foo", 1).biMapL(String::toUpperCase), is(tuple("FOO", 1))); + assertThat(tuple("foo", 1).biMapR(String::valueOf), is(tuple("foo", "1"))); + assertThat(tuple("foo", 1).biMap(String::toUpperCase, i -> i + 1), is(tuple("FOO", 2))); + } } diff --git a/src/test/java/com/jnape/palatable/lambda/applicative/BiFunctorTest.java b/src/test/java/com/jnape/palatable/lambda/applicative/BiFunctorTest.java index 422ea280f..9e23cd320 100644 --- a/src/test/java/com/jnape/palatable/lambda/applicative/BiFunctorTest.java +++ b/src/test/java/com/jnape/palatable/lambda/applicative/BiFunctorTest.java @@ -27,15 +27,4 @@ public void biMapRUsesIdentityForLeftBiMapFunction() { biFunctor.biMapR(String::valueOf); assertThat(leftInvocation.get(), is(id())); } - - @Test - public void functorProperties() { - AtomicReference leftInvocation = new AtomicReference<>(); - AtomicReference rightInvocation = new AtomicReference<>(); - BiFunctor biFunctor = new InvocationRecordingBiFunctor<>(leftInvocation, rightInvocation); - MonadicFunction fn = String::valueOf; - biFunctor.fmap(fn); - assertThat(leftInvocation.get(), is(id())); - assertThat(rightInvocation.get(), is(fn)); - } } \ No newline at end of file From 9a7393cf0256e5cfdedc74f71bfba6aae42815bf Mon Sep 17 00:00:00 2001 From: jnape Date: Sat, 18 Jun 2016 19:36:48 -0500 Subject: [PATCH 17/18] - Adding more javadocs - MonadicFunction fully overrides j.u.f.Function's interface - shuffling around packages - renaming Always to Constantly - general cleanup across the board - left/right reduce are now total - removing experimental continuations from master --- .../jnape/palatable/lambda/adt/Either.java | 20 ++++-- .../com/jnape/palatable/lambda/adt/HList.java | 11 +++- .../palatable/lambda/adt/tuples/Tuple2.java | 19 ++++-- .../palatable/lambda/adt/tuples/Tuple3.java | 10 ++- .../lambda/applicative/BiFunctor.java | 20 ------ .../palatable/lambda/applicative/Functor.java | 9 --- .../lambda/applicative/ProFunctor.java | 20 ------ .../lambda/continuation/Continuation.java | 37 ----------- .../continuation/ContinuationIterator.java | 33 ---------- .../lambda/continuation/Continuations.java | 29 --------- .../IteratorWrappingContinuation.java | 24 -------- .../palatable/lambda/continuation/Memo.java | 30 --------- .../exceptions/EmptyIterableException.java | 8 --- .../lambda/functions/DyadicFunction.java | 19 ++++-- .../lambda/functions/MonadicFunction.java | 19 +++++- .../lambda/functions/TriadicFunction.java | 10 +++ .../lambda/functions/builtin/dyadic/All.java | 2 +- .../lambda/functions/builtin/dyadic/Any.java | 2 +- .../builtin/dyadic/CartesianProduct.java | 4 +- .../lambda/functions/builtin/dyadic/Drop.java | 2 +- .../functions/builtin/dyadic/DropWhile.java | 2 +- .../functions/builtin/dyadic/Filter.java | 7 +-- .../functions/builtin/dyadic/InGroupsOf.java | 7 ++- .../functions/builtin/dyadic/Iterate.java | 2 +- .../lambda/functions/builtin/dyadic/Map.java | 2 +- .../functions/builtin/dyadic/Partial2.java | 11 +++- .../functions/builtin/dyadic/Partial3.java | 10 +++ .../functions/builtin/dyadic/ReduceLeft.java | 32 +++++++--- .../functions/builtin/dyadic/ReduceRight.java | 26 ++++++-- .../lambda/functions/builtin/dyadic/Take.java | 2 +- .../functions/builtin/dyadic/TakeWhile.java | 2 +- .../functions/builtin/dyadic/Tupler2.java | 9 ++- .../functions/builtin/dyadic/Unfoldr.java | 21 +++++++ .../lambda/functions/builtin/dyadic/Zip.java | 13 +++- .../functions/builtin/monadic/Always.java | 21 ------- .../functions/builtin/monadic/Constantly.java | 27 ++++++++ .../functions/builtin/monadic/Cycle.java | 8 ++- .../functions/builtin/monadic/Identity.java | 7 ++- .../functions/builtin/monadic/Repeat.java | 9 ++- .../functions/builtin/monadic/Reverse.java | 10 ++- .../functions/builtin/triadic/FoldLeft.java | 25 ++++++-- .../functions/builtin/triadic/FoldRight.java | 24 ++++++-- .../functions/builtin/triadic/ScanLeft.java | 25 ++++++-- .../functions/builtin/triadic/ZipWith.java | 26 +++++--- .../lambda/functions/specialized/Checked.java | 23 ------- .../functions/specialized/Predicate.java | 5 ++ .../checked/CheckedMonadicFunction.java | 28 +++++++++ .../specialized/checked/CheckedSupplier.java | 27 ++++++++ .../specialized/checked/Runtime.java | 9 +++ .../unchecked/CheckedMonadicFunction.java | 19 ------ .../unchecked/CheckedSupplier.java | 19 ------ .../palatable/lambda/functor/Bifunctor.java | 31 ++++++++++ .../palatable/lambda/functor/Functor.java | 23 +++++++ .../palatable/lambda/functor/Profunctor.java | 32 ++++++++++ .../iterators/PredicatedTakingIterator.java | 2 +- .../lambda/iterators/RewindableIterator.java | 53 ++++++++-------- .../ContinuationIteratorTest.java | 26 -------- .../lambda/continuation/ContinuationTest.java | 61 ------------------- .../continuation/ContinuationsTest.java | 49 --------------- .../lambda/continuation/MemoTest.java | 61 ------------------- .../EmptyIterableExceptionTest.java | 19 ------ .../functions/builtin/dyadic/AllTest.java | 4 +- .../functions/builtin/dyadic/AnyTest.java | 4 +- .../builtin/dyadic/DropWhileTest.java | 8 +-- .../functions/builtin/dyadic/FilterTest.java | 4 +- .../functions/builtin/dyadic/IterateTest.java | 4 +- .../builtin/dyadic/ReduceLeftTest.java | 15 +++-- .../builtin/dyadic/ReduceRightTest.java | 15 ++++- .../builtin/dyadic/TakeWhileTest.java | 8 +-- .../{AlwaysTest.java => ConstantlyTest.java} | 6 +- .../builtin/monadic/ReverseTest.java | 5 +- .../functions/specialized/CheckedTest.java | 27 -------- .../BifunctorTest.java} | 14 ++--- .../ProfunctorTest.java} | 14 ++--- ...java => InvocationRecordingBifunctor.java} | 10 +-- ...ava => InvocationRecordingProfunctor.java} | 10 +-- 76 files changed, 589 insertions(+), 702 deletions(-) delete mode 100644 src/main/java/com/jnape/palatable/lambda/applicative/BiFunctor.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/applicative/Functor.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/applicative/ProFunctor.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/continuation/Continuation.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/continuation/ContinuationIterator.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/continuation/Continuations.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/continuation/IteratorWrappingContinuation.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/continuation/Memo.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/exceptions/EmptyIterableException.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Always.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Constantly.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/functions/specialized/Checked.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/specialized/checked/CheckedMonadicFunction.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/specialized/checked/CheckedSupplier.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/specialized/checked/Runtime.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/functions/specialized/unchecked/CheckedMonadicFunction.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/functions/specialized/unchecked/CheckedSupplier.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functor/Bifunctor.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functor/Functor.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functor/Profunctor.java delete mode 100644 src/test/java/com/jnape/palatable/lambda/continuation/ContinuationIteratorTest.java delete mode 100644 src/test/java/com/jnape/palatable/lambda/continuation/ContinuationTest.java delete mode 100644 src/test/java/com/jnape/palatable/lambda/continuation/ContinuationsTest.java delete mode 100644 src/test/java/com/jnape/palatable/lambda/continuation/MemoTest.java delete mode 100644 src/test/java/com/jnape/palatable/lambda/exceptions/EmptyIterableExceptionTest.java rename src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/{AlwaysTest.java => ConstantlyTest.java} (74%) delete mode 100644 src/test/java/com/jnape/palatable/lambda/functions/specialized/CheckedTest.java rename src/test/java/com/jnape/palatable/lambda/{applicative/BiFunctorTest.java => functor/BifunctorTest.java} (60%) rename src/test/java/com/jnape/palatable/lambda/{applicative/ProFunctorTest.java => functor/ProfunctorTest.java} (60%) rename src/test/java/testsupport/applicatives/{InvocationRecordingBiFunctor.java => InvocationRecordingBifunctor.java} (67%) rename src/test/java/testsupport/applicatives/{InvocationRecordingProFunctor.java => InvocationRecordingProfunctor.java} (67%) diff --git a/src/main/java/com/jnape/palatable/lambda/adt/Either.java b/src/main/java/com/jnape/palatable/lambda/adt/Either.java index 81e850d40..2bb5b6c5f 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/Either.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/Either.java @@ -1,10 +1,10 @@ package com.jnape.palatable.lambda.adt; -import com.jnape.palatable.lambda.applicative.BiFunctor; -import com.jnape.palatable.lambda.applicative.Functor; import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; -import com.jnape.palatable.lambda.functions.specialized.unchecked.CheckedSupplier; +import com.jnape.palatable.lambda.functions.specialized.checked.CheckedSupplier; +import com.jnape.palatable.lambda.functor.Bifunctor; +import com.jnape.palatable.lambda.functor.Functor; import java.util.Objects; import java.util.Optional; @@ -12,7 +12,15 @@ import static com.jnape.palatable.lambda.functions.builtin.monadic.Identity.id; -public abstract class Either implements Functor, BiFunctor { +/** + * The binary tagged union. General semantics tend to connote "success" values via the right value and "failure" values + * via the left values. Eithers are both Functors over their right value and + * Bifunctors over both values. + * + * @param The left parameter type + * @param The right parameter type + */ +public abstract class Either implements Functor, Bifunctor { public final R or(R defaultValue) { return recover(l -> defaultValue); @@ -66,13 +74,13 @@ public final Either fmap(MonadicFunction fn @Override @SuppressWarnings("unchecked") public final Either biMapL(MonadicFunction fn) { - return (Either) BiFunctor.super.biMapL(fn); + return (Either) Bifunctor.super.biMapL(fn); } @Override @SuppressWarnings("unchecked") public final Either biMapR(MonadicFunction fn) { - return (Either) BiFunctor.super.biMapR(fn); + return (Either) Bifunctor.super.biMapR(fn); } @Override diff --git a/src/main/java/com/jnape/palatable/lambda/adt/HList.java b/src/main/java/com/jnape/palatable/lambda/adt/HList.java index 1f07671e3..13d6b4958 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/HList.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/HList.java @@ -1,8 +1,15 @@ package com.jnape.palatable.lambda.adt; -import com.jnape.palatable.lambda.applicative.Functor; import com.jnape.palatable.lambda.functions.MonadicFunction; - +import com.jnape.palatable.lambda.functor.Functor; + +/** + * A heterogeneous list supporting arbitrary depth type-safety via a linearly recursive type signature. Note that due to + * its rapidly expanding type signature, specializations exist up to certain depths to minimize typing overhead. + * + * @param The head element type + * @param The encoded recursive tail HList type + */ public abstract class HList> { private HList() { diff --git a/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple2.java b/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple2.java index 6df82af8b..e60638a5f 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple2.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple2.java @@ -1,19 +1,26 @@ package com.jnape.palatable.lambda.adt.tuples; -import com.jnape.palatable.lambda.applicative.BiFunctor; -import com.jnape.palatable.lambda.applicative.Functor; import com.jnape.palatable.lambda.functions.MonadicFunction; +import com.jnape.palatable.lambda.functor.Bifunctor; +import com.jnape.palatable.lambda.functor.Functor; import java.util.Objects; import static java.lang.String.format; -public class Tuple2<_1, _2> implements Functor<_2>, BiFunctor<_1, _2> { +/** + * The binary tuple product type. Tuple2 + * + * @param <_1> The first slot element type + * @param <_2> The second slot element type + * @see Tuple3 + */ +public class Tuple2<_1, _2> implements Functor<_2>, Bifunctor<_1, _2> { public final _1 _1; public final _2 _2; - public Tuple2(_1 _1, _2 _2) { + Tuple2(_1 _1, _2 _2) { this._1 = _1; this._2 = _2; } @@ -32,13 +39,13 @@ public <_1A, _2A> Tuple2<_1A, _2A> biMap(MonadicFunction Tuple2<_1A, _2> biMapL(MonadicFunction fn) { - return (Tuple2<_1A, _2>) BiFunctor.super.biMapL(fn); + return (Tuple2<_1A, _2>) Bifunctor.super.biMapL(fn); } @Override @SuppressWarnings("unchecked") public <_2A> Tuple2<_1, _2A> biMapR(MonadicFunction fn) { - return (Tuple2<_1, _2A>) BiFunctor.super.biMapR(fn); + return (Tuple2<_1, _2A>) Bifunctor.super.biMapR(fn); } @Override diff --git a/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple3.java b/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple3.java index bcb5954b0..a8b947414 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple3.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple3.java @@ -6,11 +6,19 @@ import static java.lang.String.format; +/** + * The ternary tuple product type. This class extends {@link Tuple2} and as such embodies the same + * functorial properties. + * + * @param <_1> The first slot element type + * @param <_2> The second slot element type + * @param <_3> The third slot element type + */ public class Tuple3<_1, _2, _3> extends Tuple2<_1, _2> { public final _3 _3; - public Tuple3(_1 _1, _2 _2, _3 _3) { + Tuple3(_1 _1, _2 _2, _3 _3) { super(_1, _2); this._3 = _3; } diff --git a/src/main/java/com/jnape/palatable/lambda/applicative/BiFunctor.java b/src/main/java/com/jnape/palatable/lambda/applicative/BiFunctor.java deleted file mode 100644 index 1a63dd236..000000000 --- a/src/main/java/com/jnape/palatable/lambda/applicative/BiFunctor.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.jnape.palatable.lambda.applicative; - -import com.jnape.palatable.lambda.functions.MonadicFunction; - -import static com.jnape.palatable.lambda.functions.builtin.monadic.Identity.id; - -@FunctionalInterface -public interface BiFunctor { - - default BiFunctor biMapL(MonadicFunction fn) { - return biMap(fn, id()); - } - - default BiFunctor biMapR(MonadicFunction fn) { - return biMap(id(), fn); - } - - BiFunctor biMap(MonadicFunction f1, - MonadicFunction f2); -} diff --git a/src/main/java/com/jnape/palatable/lambda/applicative/Functor.java b/src/main/java/com/jnape/palatable/lambda/applicative/Functor.java deleted file mode 100644 index 5dd64e16c..000000000 --- a/src/main/java/com/jnape/palatable/lambda/applicative/Functor.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.jnape.palatable.lambda.applicative; - -import com.jnape.palatable.lambda.functions.MonadicFunction; - -@FunctionalInterface -public interface Functor { - - Functor fmap(MonadicFunction fn); -} diff --git a/src/main/java/com/jnape/palatable/lambda/applicative/ProFunctor.java b/src/main/java/com/jnape/palatable/lambda/applicative/ProFunctor.java deleted file mode 100644 index f339501dd..000000000 --- a/src/main/java/com/jnape/palatable/lambda/applicative/ProFunctor.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.jnape.palatable.lambda.applicative; - -import com.jnape.palatable.lambda.functions.MonadicFunction; - -import static com.jnape.palatable.lambda.functions.builtin.monadic.Identity.id; - -@FunctionalInterface -public interface ProFunctor { - - default ProFunctor diMapL(MonadicFunction fn) { - return diMap(fn, id()); - } - - default ProFunctor diMapR(MonadicFunction fn) { - return diMap(id(), fn); - } - - ProFunctor diMap(MonadicFunction f1, - MonadicFunction f2); -} diff --git a/src/main/java/com/jnape/palatable/lambda/continuation/Continuation.java b/src/main/java/com/jnape/palatable/lambda/continuation/Continuation.java deleted file mode 100644 index bf9391909..000000000 --- a/src/main/java/com/jnape/palatable/lambda/continuation/Continuation.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.jnape.palatable.lambda.continuation; - -import com.jnape.palatable.lambda.adt.tuples.Tuple2; -import com.jnape.palatable.lambda.applicative.Functor; -import com.jnape.palatable.lambda.functions.MonadicFunction; - -import java.util.Iterator; -import java.util.Optional; - -import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; -import static com.jnape.palatable.lambda.continuation.Memo.memoize; - -@FunctionalInterface -public interface Continuation extends Functor, Iterable { - - Optional>> next(); - - @Override - default Iterator iterator() { - return ContinuationIterator.wrap(this); - } - - default Continuation then(Continuation more) { - return () -> next() - .map(t -> Optional.of(tuple(t._1, t._2.then(more)))) - .orElseGet(more::next); - } - - @Override - default Continuation fmap(MonadicFunction fn) { - return (() -> next().map(t -> t.biMap(fn, c -> c.fmap(fn)))); - } - - default Continuation memoized() { - return memoize((() -> next().map(t -> tuple(t._1, t._2.memoized()))))::get; - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/continuation/ContinuationIterator.java b/src/main/java/com/jnape/palatable/lambda/continuation/ContinuationIterator.java deleted file mode 100644 index 6fae52eb7..000000000 --- a/src/main/java/com/jnape/palatable/lambda/continuation/ContinuationIterator.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.jnape.palatable.lambda.continuation; - -import com.jnape.palatable.lambda.adt.tuples.Tuple2; - -import java.util.Iterator; -import java.util.NoSuchElementException; - -public final class ContinuationIterator implements Iterator { - private Continuation continuation; - - public ContinuationIterator(Continuation continuation) { - this.continuation = continuation; - } - - @Override - public boolean hasNext() { - return continuation.next().isPresent(); - } - - @Override - public A next() { - if (!hasNext()) - throw new NoSuchElementException(); - - Tuple2> continuationResult = continuation.next().get(); - continuation = continuationResult._2; - return continuationResult._1; - } - - public static ContinuationIterator wrap(Continuation continuable) { - return new ContinuationIterator<>(continuable); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/continuation/Continuations.java b/src/main/java/com/jnape/palatable/lambda/continuation/Continuations.java deleted file mode 100644 index f788876a9..000000000 --- a/src/main/java/com/jnape/palatable/lambda/continuation/Continuations.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.jnape.palatable.lambda.continuation; - -import java.util.Iterator; -import java.util.stream.Stream; - -import static java.util.Arrays.asList; - -public final class Continuations { - - private Continuations() { - } - - @SafeVarargs - public static Continuation continuing(A... as) { - return continuing(asList(as)); - } - - public static Continuation continuing(Stream as) { - return continuing(as.iterator()); - } - - public static Continuation continuing(Iterable as) { - return continuing(as.iterator()); - } - - public static Continuation continuing(Iterator as) { - return new IteratorWrappingContinuation<>(as); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/continuation/IteratorWrappingContinuation.java b/src/main/java/com/jnape/palatable/lambda/continuation/IteratorWrappingContinuation.java deleted file mode 100644 index a7c1b8114..000000000 --- a/src/main/java/com/jnape/palatable/lambda/continuation/IteratorWrappingContinuation.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.jnape.palatable.lambda.continuation; - -import com.jnape.palatable.lambda.adt.tuples.Tuple2; - -import java.util.Iterator; -import java.util.Optional; - -import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; -import static com.jnape.palatable.lambda.continuation.Memo.memoize; - -public final class IteratorWrappingContinuation implements Continuation { - private final Memo>>> memoizedResult; - - public IteratorWrappingContinuation(Iterator iterator) { - memoizedResult = memoize(() -> (iterator.hasNext()) - ? Optional.of(tuple(iterator.next(), new IteratorWrappingContinuation<>(iterator))) - : Optional.empty()); - } - - @Override - public Optional>> next() { - return memoizedResult.get(); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/continuation/Memo.java b/src/main/java/com/jnape/palatable/lambda/continuation/Memo.java deleted file mode 100644 index 95124052c..000000000 --- a/src/main/java/com/jnape/palatable/lambda/continuation/Memo.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.jnape.palatable.lambda.continuation; - -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Supplier; - -public final class Memo { - private final AtomicReference valueRef; - private final Supplier fn; - - private Memo(Supplier fn) { - this.fn = fn; - valueRef = new AtomicReference<>(); - } - - public T get() { - if (valueRef.get() == null) - valueRef.compareAndSet(null, fn.get()); - return valueRef.get(); - } - - public static Memo memoize(Supplier fn) { - return new Memo<>(fn); - } - - public static Memo memoize(T result) { - return memoize(() -> result); - } - - -} diff --git a/src/main/java/com/jnape/palatable/lambda/exceptions/EmptyIterableException.java b/src/main/java/com/jnape/palatable/lambda/exceptions/EmptyIterableException.java deleted file mode 100644 index 267145b4a..000000000 --- a/src/main/java/com/jnape/palatable/lambda/exceptions/EmptyIterableException.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.jnape.palatable.lambda.exceptions; - -public class EmptyIterableException extends RuntimeException { - - public EmptyIterableException() { - super("Iterable was empty."); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java b/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java index b7a669874..3aed1de61 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java @@ -1,10 +1,19 @@ package com.jnape.palatable.lambda.functions; import com.jnape.palatable.lambda.adt.tuples.Tuple2; -import com.jnape.palatable.lambda.applicative.ProFunctor; - +import com.jnape.palatable.lambda.functor.Profunctor; + +/** + * A function taking two arguments. Auto-curried. + * + * @param The first argument type + * @param The second argument type + * @param The return type + * @see MonadicFunction + * @see com.jnape.palatable.lambda.functions.builtin.dyadic.Partial2 + */ @FunctionalInterface -public interface DyadicFunction extends MonadicFunction>, ProFunctor { +public interface DyadicFunction extends MonadicFunction>, Profunctor { C apply(A a, B b); @@ -16,13 +25,13 @@ default MonadicFunction apply(A a) { @Override @SuppressWarnings("unchecked") default DyadicFunction diMapL(MonadicFunction fn) { - return (DyadicFunction) ProFunctor.super.diMapL(fn); + return (DyadicFunction) Profunctor.super.diMapL(fn); } @Override @SuppressWarnings("unchecked") default DyadicFunction diMapR(MonadicFunction fn) { - return (DyadicFunction) ProFunctor.super.diMapR(fn); + return (DyadicFunction) Profunctor.super.diMapR(fn); } @Override diff --git a/src/main/java/com/jnape/palatable/lambda/functions/MonadicFunction.java b/src/main/java/com/jnape/palatable/lambda/functions/MonadicFunction.java index f6c142d63..dddd2ec8c 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/MonadicFunction.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/MonadicFunction.java @@ -1,9 +1,16 @@ package com.jnape.palatable.lambda.functions; -import com.jnape.palatable.lambda.applicative.Functor; +import com.jnape.palatable.lambda.functor.Functor; import java.util.function.Function; +/** + * A function taking a single argument. This is the core function type that all other function types extend and + * auto-curry with. + * + * @param The input type + * @param The output type + */ @FunctionalInterface public interface MonadicFunction extends Functor, Function { @@ -17,4 +24,14 @@ default MonadicFunction then(MonadicFunction g default MonadicFunction fmap(MonadicFunction g) { return a -> g.apply(apply(a)); } + + @Override + default MonadicFunction compose(Function before) { + return z -> apply(before.apply(z)); + } + + @Override + default MonadicFunction andThen(Function after) { + return a -> after.apply(apply(a)); + } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/TriadicFunction.java b/src/main/java/com/jnape/palatable/lambda/functions/TriadicFunction.java index 4ca576955..db3cb325d 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/TriadicFunction.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/TriadicFunction.java @@ -2,6 +2,16 @@ import com.jnape.palatable.lambda.adt.tuples.Tuple2; +/** + * A function taking three arguments. Auto-curried + * + * @param The first argument type + * @param The second argument type + * @param The third argument type + * @param The return type + * @see DyadicFunction + * @see com.jnape.palatable.lambda.functions.builtin.dyadic.Partial3 + */ @FunctionalInterface public interface TriadicFunction extends DyadicFunction> { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/All.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/All.java index 9b82a21e1..768a76b0b 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/All.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/All.java @@ -17,7 +17,7 @@ private All() { } @Override - public final Boolean apply(MonadicFunction predicate, Iterable as) { + public Boolean apply(MonadicFunction predicate, Iterable as) { for (A a : as) if (!predicate.apply(a)) return false; diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Any.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Any.java index c3ecde1f7..110111adb 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Any.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Any.java @@ -17,7 +17,7 @@ private Any() { } @Override - public final Boolean apply(MonadicFunction predicate, Iterable as) { + public Boolean apply(MonadicFunction predicate, Iterable as) { for (A a : as) if (predicate.apply(a)) return true; diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java index 6230a3fa6..159e1f8b6 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java @@ -9,7 +9,7 @@ * Lazily compute the cartesian product of an Iterable<A> and Iterable<B>, * returning an Iterable<Tuple2<A, B>>, the products as tuples of * multiplicand As and multiplier Bs. - *

+ *

* Note that this algorithm exhaustively pairs all elements from Iterable<B> to the first element of * Iterable<A> before advancing to the next element of Iterable<A>, so if * Iterable<B> is infinite, only one element from Iterable<A> will ever be @@ -25,7 +25,7 @@ private CartesianProduct() { } @Override - public final Iterable> apply(Iterable as, Iterable bs) { + public Iterable> apply(Iterable as, Iterable bs) { return () -> new CombinatorialIterator<>(as.iterator(), bs.iterator()); } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Drop.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Drop.java index 146d9b396..537fa015e 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Drop.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Drop.java @@ -19,7 +19,7 @@ private Drop() { } @Override - public final Iterable apply(final Integer n, final Iterable as) { + public Iterable apply(Integer n, Iterable as) { return () -> new DroppingIterator<>(n, as.iterator()); } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhile.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhile.java index ef5d28dbe..676eef969 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhile.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhile.java @@ -20,7 +20,7 @@ private DropWhile() { } @Override - public final Iterable apply(final MonadicFunction predicate, final Iterable as) { + public Iterable apply(MonadicFunction predicate, Iterable as) { return () -> new PredicatedDroppingIterator<>(predicate, as.iterator()); } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Filter.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Filter.java index bc965e7f3..394faf487 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Filter.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Filter.java @@ -18,7 +18,7 @@ private Filter() { } @Override - public final Iterable apply(final MonadicFunction predicate, final Iterable as) { + public Iterable apply(MonadicFunction predicate, Iterable as) { return () -> new FilteringIterator<>(predicate, as.iterator()); } @@ -26,12 +26,11 @@ public static Filter filter() { return new Filter<>(); } - public static MonadicFunction, Iterable> filter( - final MonadicFunction predicate) { + public static MonadicFunction, Iterable> filter(MonadicFunction predicate) { return Filter.filter().apply(predicate); } - public static Iterable filter(final MonadicFunction predicate, final Iterable as) { + public static Iterable filter(MonadicFunction predicate, Iterable as) { return Filter.filter(predicate).apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/InGroupsOf.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/InGroupsOf.java index c1a0fe5ea..f2438aead 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/InGroupsOf.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/InGroupsOf.java @@ -6,8 +6,9 @@ /** * Lazily group the Iterable by returning an Iterable of smaller Iterables of - * size k. Note that groups are not padded; that is, if k >= n, where n - * is the number of remaining elements, the final Iterable will have only n elements. + * size k. Note that groups are not padded; that is, if k >= n, where + * n is the number of remaining elements, the final Iterable will have only n + * elements. * * @param The Iterable element type */ @@ -17,7 +18,7 @@ private InGroupsOf() { } @Override - public final Iterable> apply(final Integer k, final Iterable as) { + public Iterable> apply(Integer k, Iterable as) { return () -> new GroupingIterator<>(k, as.iterator()); } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java index 79d0716a5..22e2a169a 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java @@ -21,7 +21,7 @@ private Iterate() { } @Override - public final Iterable apply(final MonadicFunction fn, final A seed) { + public Iterable apply(MonadicFunction fn, A seed) { return () -> new UnfoldingIterator<>(a -> Optional.of(tuple(a, fn.apply(a))), seed); } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Map.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Map.java index 2a6797d37..47e77e3b8 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Map.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Map.java @@ -17,7 +17,7 @@ private Map() { } @Override - public final Iterable apply(final MonadicFunction function, final Iterable as) { + public Iterable apply(MonadicFunction function, Iterable as) { return () -> new MappingIterator<>(function, as.iterator()); } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial2.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial2.java index 4f198ff56..2990dac51 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial2.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial2.java @@ -3,6 +3,15 @@ import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; +/** + * Partially apply (fix) the first argument of a DyadicFunction, producing a MonadicFunction + * that takes the remaining argument. This is isomorphic to calling {@link com.jnape.palatable.lambda.functions.DyadicFunction#apply(Object)}. + * + * @param The type of the value to be supplied + * @param The input argument type of the resulting function + * @param The return type of the resulting function + * @see Partial3 + */ public final class Partial2 implements DyadicFunction, A, MonadicFunction> { private Partial2() { @@ -21,7 +30,7 @@ public static MonadicFunction> partial2(Dyadi return Partial2.partial2().apply(new Partial2<>(), function); } - public static MonadicFunction partial2(DyadicFunction function, final A a) { + public static MonadicFunction partial2(DyadicFunction function, A a) { return partial2(function).apply(a); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial3.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial3.java index db455b84e..cb1423b01 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial3.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial3.java @@ -4,6 +4,16 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.functions.TriadicFunction; +/** + * Partially apply (fix) the first argument of a TriadicFunction, producing a DyadicFunction + * that takes the remaining two argument. This is isomorphic to calling {@link com.jnape.palatable.lambda.functions.TriadicFunction#apply(Object)}. + * + * @param The type of the value to be supplied + * @param The first input argument type of the resulting function + * @param The second input argument type of the resulting function + * @param The return type of the resulting function + * @see Partial2 + */ public final class Partial3 implements DyadicFunction, A, DyadicFunction> { private Partial3() { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeft.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeft.java index cda2c7be2..40615afbb 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeft.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeft.java @@ -1,38 +1,52 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; -import com.jnape.palatable.lambda.exceptions.EmptyIterableException; import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; import java.util.Iterator; +import java.util.Optional; import static com.jnape.palatable.lambda.functions.builtin.triadic.FoldLeft.foldLeft; -public final class ReduceLeft implements DyadicFunction, Iterable, A> { +/** + * Given an Iterable of As and a {@link DyadicFunction}<A, A, A>, + * iteratively accumulate over the Iterable, returning an Optional<A> (if the + * Iterable is empty, the result is Optional.empty(); otherwise, the result is wrapped in + * Optional.of(). For this reason, null accumulation results are considered erroneous and will + * throw. + *

+ * This function is isomorphic to a left fold over the Iterable where the head element is the starting + * accumulation value and the result is lifted into an Optional. + * + * @param The input Iterable element type, as well as the accumulation type + * @see ReduceRight + * @see com.jnape.palatable.lambda.functions.builtin.triadic.FoldLeft + */ +public final class ReduceLeft implements DyadicFunction, Iterable, Optional> { private ReduceLeft() { } @Override - public final A apply(DyadicFunction function, Iterable as) { - final Iterator iterator = as.iterator(); - + public Optional apply(DyadicFunction function, Iterable as) { + Iterator iterator = as.iterator(); if (!iterator.hasNext()) - throw new EmptyIterableException(); + return Optional.empty(); - return foldLeft(function, iterator.next(), () -> iterator); + return Optional.of(foldLeft(function, iterator.next(), () -> iterator)); } public static ReduceLeft reduceLeft() { return new ReduceLeft<>(); } - public static MonadicFunction, A> reduceLeft( + public static MonadicFunction, Optional> reduceLeft( DyadicFunction function) { return ReduceLeft.reduceLeft().apply(function); } - public static A reduceLeft(DyadicFunction function, Iterable as) { + public static Optional reduceLeft(DyadicFunction function, + Iterable as) { return ReduceLeft.reduceLeft(function).apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceRight.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceRight.java index 9c20ed2f5..30606f018 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceRight.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceRight.java @@ -3,16 +3,33 @@ import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; +import java.util.Optional; + import static com.jnape.palatable.lambda.functions.builtin.dyadic.ReduceLeft.reduceLeft; import static com.jnape.palatable.lambda.functions.builtin.monadic.Reverse.reverse; -public final class ReduceRight implements DyadicFunction, Iterable, A> { +/** + * Given an Iterable of As and a {@link DyadicFunction}<A, A, A>, + * iteratively accumulate over the Iterable, returning an Optional<A> + * (if the + * Iterable is empty, the result is Optional.empty(); otherwise, the result is wrapped in + * Optional.of(). For this reason, null accumulation results are considered erroneous and will + * throw. + *

+ * This function is isomorphic to a right fold over the Iterable where the tail element is the starting + * accumulation value and the result is lifted into an Optional. + * + * @param The input Iterable element type, as well as the accumulation type + * @see ReduceLeft + * @see com.jnape.palatable.lambda.functions.builtin.triadic.FoldRight + */ +public final class ReduceRight implements DyadicFunction, Iterable, Optional> { private ReduceRight() { } @Override - public final A apply(DyadicFunction function, Iterable as) { + public final Optional apply(DyadicFunction function, Iterable as) { return reduceLeft(function.flip(), reverse(as)); } @@ -20,12 +37,13 @@ public static ReduceRight reduceRight() { return new ReduceRight<>(); } - public static MonadicFunction, A> reduceRight( + public static MonadicFunction, Optional> reduceRight( DyadicFunction function) { return ReduceRight.reduceRight().apply(function); } - public static A reduceRight(DyadicFunction function, Iterable as) { + public static Optional reduceRight(DyadicFunction function, + Iterable as) { return ReduceRight.reduceRight(function).apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Take.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Take.java index defc6c71b..c0b860ca1 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Take.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Take.java @@ -19,7 +19,7 @@ private Take() { } @Override - public final Iterable apply(final Integer n, final Iterable as) { + public Iterable apply(Integer n, Iterable as) { return () -> new TakingIterator<>(n, as.iterator()); } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhile.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhile.java index c7db67e7b..335983e6e 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhile.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhile.java @@ -19,7 +19,7 @@ private TakeWhile() { } @Override - public final Iterable apply(final MonadicFunction predicate, final Iterable as) { + public Iterable apply(MonadicFunction predicate, Iterable as) { return () -> new PredicatedTakingIterator<>(predicate, as.iterator()); } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java index 38a56db23..6a512a16e 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java @@ -6,13 +6,20 @@ import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; +/** + * Creates a Tuple2 from two values. + * + * @param The type of the first value; also the first slot type of returned Tuple2 + * @param The type of the second value; also the second slot type of returned Tuple2 + * @see Tuple2 + */ public final class Tupler2 implements DyadicFunction> { private Tupler2() { } @Override - public final Tuple2 apply(A a, B b) { + public Tuple2 apply(A a, B b) { return tuple(a, b); } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java index 502fdd1b8..3a87da9ea 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java @@ -7,6 +7,27 @@ import java.util.Optional; +/** + * Given an initial seed value and a function that takes the seed type and produces an Optional<{@link + * Tuple2}<X, Seed>>, where the tuple's first slot represents the next Iterable element, + * and the second slot represents the next input to the unfolding function, unfold an Iterable of + * Xs. Returning Optional.empty() from the unfolding function is a signal that the + * Iterable is fully unfolded. + *

+ * For more information, read about Anamorphisms. + *

+ * Example: + *

+ * {@code
+ * Iterable zeroThroughTenInclusive = unfoldr(x -> x <= 10
+ *         ? Optional.of(tuple(x, x + 1))
+ *         : Optional.empty(), 0);
+ * }
+ * 
+ * + * @param The output Iterable element type + * @param The unfolding function input type + */ public final class Unfoldr implements DyadicFunction>>, B, Iterable> { private Unfoldr() { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Zip.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Zip.java index 3274df152..a82c89d2f 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Zip.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Zip.java @@ -7,13 +7,22 @@ import static com.jnape.palatable.lambda.functions.builtin.dyadic.Tupler2.tupler; import static com.jnape.palatable.lambda.functions.builtin.triadic.ZipWith.zipWith; +/** + * Zip together two Iterables into a single Iterable of Tuple2<A, B>. If + * the input Iterables differ in size, the resulting Iterable contains only as many pairs as + * the smallest input Iterable's elements. + * + * @param The first input Iterable element type, and the type of the first tuple slot in the output Iterable + * @param The second input Iterable element type, and the type of the second tuple slot in the output Iterable + * @see com.jnape.palatable.lambda.functions.builtin.triadic.ZipWith + */ public final class Zip implements DyadicFunction, Iterable, Iterable>> { private Zip() { } @Override - public final Iterable> apply(final Iterable as, final Iterable bs) { + public Iterable> apply(Iterable as, Iterable bs) { return zipWith(tupler(), as, bs); } @@ -26,6 +35,6 @@ public static MonadicFunction, Iterable>> zip(It } public static Iterable> zip(Iterable as, Iterable bs) { - return Zip.zip().apply(as, bs); + return Zip.zip(as).apply(bs); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Always.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Always.java deleted file mode 100644 index c101f63ea..000000000 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Always.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.jnape.palatable.lambda.functions.builtin.monadic; - -import com.jnape.palatable.lambda.functions.MonadicFunction; - -public final class Always implements MonadicFunction { - - private final B b; - - public Always(B b) { - this.b = b; - } - - @Override - public final B apply(A a) { - return b; - } - - public static MonadicFunction always(B b) { - return new Always<>(b); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Constantly.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Constantly.java new file mode 100644 index 000000000..104bbf416 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Constantly.java @@ -0,0 +1,27 @@ +package com.jnape.palatable.lambda.functions.builtin.monadic; + +import com.jnape.palatable.lambda.functions.MonadicFunction; + +/** + * Given a value, produce a function that returns that value, regardless of input. + * + * @param The input type of the resulting function + * @param The output type of the resulting function + */ +public final class Constantly implements MonadicFunction { + + private final B b; + + public Constantly(B b) { + this.b = b; + } + + @Override + public B apply(A a) { + return b; + } + + public static MonadicFunction constantly(B b) { + return new Constantly<>(b); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Cycle.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Cycle.java index 919e4ba0b..cc1694f79 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Cycle.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Cycle.java @@ -5,10 +5,16 @@ import static java.util.Arrays.asList; +/** + * Given an Iterable, return an infinite Iterable that repeatedly cycles its elements, in + * order. + * + * @param The Iterable element type + */ public final class Cycle implements MonadicFunction, Iterable> { @Override - public final Iterable apply(final Iterable as) { + public Iterable apply(Iterable as) { return () -> new CyclicIterator<>(as.iterator()); } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Identity.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Identity.java index 175ae181f..1f454e380 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Identity.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Identity.java @@ -2,12 +2,17 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; +/** + * The identity function. + * + * @param The input/output type + */ public final class Identity implements MonadicFunction { private static final Identity IDENTITY = new Identity(); @Override - public final A apply(A a) { + public A apply(A a) { return a; } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Repeat.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Repeat.java index 51f87ffdb..9f06e6e64 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Repeat.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Repeat.java @@ -3,10 +3,15 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.RepetitiousIterator; +/** + * Given a value, return an infinite Iterable that repeatedly iterates that value. + * + * @param The Iterable element type + */ public final class Repeat implements MonadicFunction> { @Override - public final Iterable apply(final A a) { + public Iterable apply(A a) { return () -> new RepetitiousIterator<>(a); } @@ -14,7 +19,7 @@ public static Repeat repeat() { return new Repeat<>(); } - public static Iterable repeat(final A a) { + public static Iterable repeat(A a) { return Repeat.repeat().apply(a); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Reverse.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Reverse.java index 47428d88f..ad15f59c0 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Reverse.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/monadic/Reverse.java @@ -3,10 +3,16 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.ReversingIterator; +/** + * Given an Iterable, return a reversed representation of that Iterable. Note that reversing + * is deferred until the returned Iterable is iterated. + * + * @param The Iterable element type + */ public final class Reverse implements MonadicFunction, Iterable> { @Override - public final Iterable apply(final Iterable as) { + public Iterable apply(Iterable as) { return () -> new ReversingIterator<>(as.iterator()); } @@ -14,7 +20,7 @@ public static Reverse reverse() { return new Reverse<>(); } - public static Iterable reverse(final Iterable as) { + public static Iterable reverse(Iterable as) { return Reverse.reverse().apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/FoldLeft.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/FoldLeft.java index f47bbe9f9..d6c6810d1 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/FoldLeft.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/FoldLeft.java @@ -4,11 +4,25 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.functions.TriadicFunction; +/** + * Given an Iterable of As, a starting value B, and a {@link + * DyadicFunction}<B, A, B>, iteratively accumulate over the Iterable, ultimately returning a + * final B value. If the Iterable is empty, just return the starting B value. + * Note that, as the name implies, this function accumulates from left to right, such that foldLeft(f, 0, + * asList(1, 2, 3, 4, 5)) is evaluated as f(f(f(f(f(0, 1), 2), 3), 4), 5). + *

+ * For more information, read about Catamorphisms. + * + * @param The Iterable element type + * @param The accumulation type + * @see FoldRight + */ public final class FoldLeft implements TriadicFunction, B, Iterable, B> { @Override - public final B apply(DyadicFunction function, B initialAccumulation, - Iterable as) { + public B apply(DyadicFunction function, B initialAccumulation, + Iterable as) { B accumulation = initialAccumulation; for (A a : as) accumulation = function.apply(accumulation, a); @@ -25,13 +39,12 @@ public static DyadicFunction, B> foldLeft( } public static MonadicFunction, B> foldLeft( - DyadicFunction function, - B initialAccumulation) { - return foldLeft(function).apply(initialAccumulation); + DyadicFunction function, B initialAccumulation) { + return FoldLeft.foldLeft(function).apply(initialAccumulation); } public static B foldLeft(DyadicFunction function, B initialAccumulation, Iterable as) { - return foldLeft(function, initialAccumulation).apply(as); + return FoldLeft.foldLeft(function, initialAccumulation).apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/FoldRight.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/FoldRight.java index fd4254653..657f740ea 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/FoldRight.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/FoldRight.java @@ -7,11 +7,24 @@ import static com.jnape.palatable.lambda.functions.builtin.monadic.Reverse.reverse; import static com.jnape.palatable.lambda.functions.builtin.triadic.FoldLeft.foldLeft; +/** + * Given an Iterable of As, a starting value B, and a {@link + * DyadicFunction}<A, B, B>, iteratively accumulate over the Iterable, ultimately returning a + * final B value. If the Iterable is empty, just return the starting B value. + * This function is the iterative inverse of {@link FoldLeft}, such that foldRight(f, 0, asList(1, 2, 3, 4, + * 5)) is evaluated as f(f(f(f(f(0, 5), 4), 3), 2), 1). + *

+ * For more information, read about Catamorphisms. + * + * @param The Iterable element type + * @param The accumulation type + * @see FoldLeft + */ public final class FoldRight implements TriadicFunction, B, Iterable, B> { @Override - public final B apply(DyadicFunction function, B initialAccumulation, - Iterable as) { + public B apply(DyadicFunction function, B initialAccumulation, Iterable as) { return foldLeft(function.flip(), initialAccumulation, reverse(as)); } @@ -25,13 +38,12 @@ public static DyadicFunction, B> foldRight( } public static MonadicFunction, B> foldRight( - DyadicFunction function, - B initialAccumulation) { - return foldRight(function).apply(initialAccumulation); + DyadicFunction function, B initialAccumulation) { + return FoldRight.foldRight(function).apply(initialAccumulation); } public static B foldRight(DyadicFunction function, B initialAccumulation, Iterable as) { - return foldRight(function, initialAccumulation).apply(as); + return FoldRight.foldRight(function, initialAccumulation).apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/ScanLeft.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/ScanLeft.java index d6d16e722..d00dd9fa9 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/ScanLeft.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/ScanLeft.java @@ -5,6 +5,20 @@ import com.jnape.palatable.lambda.functions.TriadicFunction; import com.jnape.palatable.lambda.iterators.ScanningIterator; +import static java.util.Arrays.asList; + +/** + * Given an Iterable of As, a starting value B, and a {@link + * DyadicFunction}<B, A, B>, iteratively accumulate over the Iterable, collecting each + * function application result, finally returning an Iterable of all the results. Note that, as the name + * implies, this function accumulates from left to right, such that scanLeft(f, 0, asList(1,2,3,4,5)) is + * evaluated as 0, f(0, 1), f(f(0, 1), 2), f(f(f(0, 1), 2), 3), f(f(f(f(0, 1), 2), 3), 4), f(f(f(f(f(0, 1), 2), 3), 4), + * 5). + * + * @param The Iterable element type + * @param The accumulation type + * @see FoldLeft + */ public final class ScanLeft implements TriadicFunction, B, Iterable, Iterable> { @Override @@ -23,12 +37,15 @@ public static DyadicFunction, Iterable> scanLeft( public static MonadicFunction, Iterable> scanLeft( DyadicFunction fn, B b) { - return scanLeft(fn).apply(b); + return ScanLeft.scanLeft(fn).apply(b); } - public static Iterable scanLeft(DyadicFunction fn, - B b, + public static Iterable scanLeft(DyadicFunction fn, B b, Iterable as) { - return scanLeft(fn, b).apply(as); + return ScanLeft.scanLeft(fn, b).apply(as); + } + + public static void main(String[] args) { + scanLeft((acc, x) -> "f(" + acc + ", " + x + ")", "0", asList(1, 2, 3, 4, 5)).forEach(System.out::println); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/ZipWith.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/ZipWith.java index d1e205eae..817218f1e 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/ZipWith.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/triadic/ZipWith.java @@ -5,12 +5,20 @@ import com.jnape.palatable.lambda.functions.TriadicFunction; import com.jnape.palatable.lambda.iterators.ZippingIterator; +/** + * Zip together two Iterables by applying a zipping function to the successive elements of each + * Iterable until one of them runs out of elements. Returns an Iterable containing the + * results. + * + * @param The first input Iterable element type + * @param The second input Iterable element type + * @param The output Iterable element type + * @see com.jnape.palatable.lambda.functions.builtin.dyadic.Zip + */ public final class ZipWith implements TriadicFunction, Iterable, Iterable, Iterable> { @Override - public final Iterable apply(final DyadicFunction zipper, - final Iterable as, - final Iterable bs) { + public Iterable apply(DyadicFunction zipper, Iterable as, Iterable bs) { return () -> new ZippingIterator<>(zipper, as.iterator(), bs.iterator()); } @@ -24,14 +32,12 @@ public static DyadicFunction, Iterable, Iterable> zi } public static MonadicFunction, Iterable> zipWith( - final DyadicFunction zipper, - final Iterable as) { - return zipWith(zipper).apply(as); + DyadicFunction zipper, Iterable as) { + return ZipWith.zipWith(zipper).apply(as); } - public static Iterable zipWith(final DyadicFunction zipper, - final Iterable as, - final Iterable bs) { - return zipWith(zipper, as).apply(bs); + public static Iterable zipWith(DyadicFunction zipper, + Iterable as, Iterable bs) { + return ZipWith.zipWith(zipper, as).apply(bs); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/specialized/Checked.java b/src/main/java/com/jnape/palatable/lambda/functions/specialized/Checked.java deleted file mode 100644 index b16d66a24..000000000 --- a/src/main/java/com/jnape/palatable/lambda/functions/specialized/Checked.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.jnape.palatable.lambda.functions.specialized; - -import com.jnape.palatable.lambda.functions.MonadicFunction; -import com.jnape.palatable.lambda.functions.specialized.unchecked.CheckedMonadicFunction; -import com.jnape.palatable.lambda.functions.specialized.unchecked.CheckedSupplier; - -import java.util.function.Supplier; - -public class Checked { - - public static CheckedSupplier checked(Supplier supplier) { - return supplier::get; - } - - public static CheckedMonadicFunction checked(MonadicFunction fn) { - return fn::apply; - } - - @SuppressWarnings("unchecked") - public static RuntimeException throwChecked(Throwable ex) throws T { - throw (T) ex; - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/specialized/Predicate.java b/src/main/java/com/jnape/palatable/lambda/functions/specialized/Predicate.java index 5e62430b9..5b3095972 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/specialized/Predicate.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/specialized/Predicate.java @@ -2,6 +2,11 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; +/** + * A specialized {@link MonadicFunction} that returns a Boolean. + * + * @param The argument type + */ public interface Predicate extends MonadicFunction, java.util.function.Predicate { @Override diff --git a/src/main/java/com/jnape/palatable/lambda/functions/specialized/checked/CheckedMonadicFunction.java b/src/main/java/com/jnape/palatable/lambda/functions/specialized/checked/CheckedMonadicFunction.java new file mode 100644 index 000000000..c5013e4ad --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/specialized/checked/CheckedMonadicFunction.java @@ -0,0 +1,28 @@ +package com.jnape.palatable.lambda.functions.specialized.checked; + +import com.jnape.palatable.lambda.functions.MonadicFunction; + +import static com.jnape.palatable.lambda.functions.specialized.checked.Runtime.throwChecked; + +/** + * Specialized {@link MonadicFunction} that can throw checked exceptions. + * + * @param The input type + * @param The output type + * @see CheckedSupplier + * @see MonadicFunction + */ +@FunctionalInterface +public interface CheckedMonadicFunction extends MonadicFunction { + + @Override + default B apply(A a) { + try { + return checkedApply(a); + } catch (Throwable t) { + throw throwChecked(t); + } + } + + B checkedApply(A a) throws Exception; +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/specialized/checked/CheckedSupplier.java b/src/main/java/com/jnape/palatable/lambda/functions/specialized/checked/CheckedSupplier.java new file mode 100644 index 000000000..4009538b1 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/specialized/checked/CheckedSupplier.java @@ -0,0 +1,27 @@ +package com.jnape.palatable.lambda.functions.specialized.checked; + +import java.util.function.Supplier; + +import static com.jnape.palatable.lambda.functions.specialized.checked.Runtime.throwChecked; + +/** + * Specialized {@link Supplier} that can throw checked exceptions. + * + * @param The exception type + * @param The return type + * @see CheckedMonadicFunction + */ +@FunctionalInterface +public interface CheckedSupplier extends Supplier { + + @Override + default T get() { + try { + return checkedGet(); + } catch (Throwable t) { + throw throwChecked(t); + } + } + + T checkedGet() throws E; +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/specialized/checked/Runtime.java b/src/main/java/com/jnape/palatable/lambda/functions/specialized/checked/Runtime.java new file mode 100644 index 000000000..810528676 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/specialized/checked/Runtime.java @@ -0,0 +1,9 @@ +package com.jnape.palatable.lambda.functions.specialized.checked; + +class Runtime { + + @SuppressWarnings("unchecked") + public static RuntimeException throwChecked(Throwable ex) throws T { + throw (T) ex; + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/specialized/unchecked/CheckedMonadicFunction.java b/src/main/java/com/jnape/palatable/lambda/functions/specialized/unchecked/CheckedMonadicFunction.java deleted file mode 100644 index 7182f4542..000000000 --- a/src/main/java/com/jnape/palatable/lambda/functions/specialized/unchecked/CheckedMonadicFunction.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.jnape.palatable.lambda.functions.specialized.unchecked; - -import com.jnape.palatable.lambda.functions.MonadicFunction; - -import static com.jnape.palatable.lambda.functions.specialized.Checked.throwChecked; - -public interface CheckedMonadicFunction extends MonadicFunction { - - @Override - default B apply(A a) { - try { - return checkedApply(a); - } catch (Throwable t) { - throw throwChecked(t); - } - } - - B checkedApply(A a) throws Throwable; -} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/specialized/unchecked/CheckedSupplier.java b/src/main/java/com/jnape/palatable/lambda/functions/specialized/unchecked/CheckedSupplier.java deleted file mode 100644 index 1ff288815..000000000 --- a/src/main/java/com/jnape/palatable/lambda/functions/specialized/unchecked/CheckedSupplier.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.jnape.palatable.lambda.functions.specialized.unchecked; - -import java.util.function.Supplier; - -import static com.jnape.palatable.lambda.functions.specialized.Checked.throwChecked; - -public interface CheckedSupplier extends Supplier { - - @Override - default T get() { - try { - return checkedGet(); - } catch (Throwable t) { - throw throwChecked(t); - } - } - - T checkedGet() throws E; -} diff --git a/src/main/java/com/jnape/palatable/lambda/functor/Bifunctor.java b/src/main/java/com/jnape/palatable/lambda/functor/Bifunctor.java new file mode 100644 index 000000000..3ba5547da --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functor/Bifunctor.java @@ -0,0 +1,31 @@ +package com.jnape.palatable.lambda.functor; + +import com.jnape.palatable.lambda.functions.MonadicFunction; + +import static com.jnape.palatable.lambda.functions.builtin.monadic.Identity.id; + +/** + * A dually-parametric Functor that maps covariantly over both parameters. + *

+ * For more information, read about Bifunctors. + * + * @param The type of the first parameter + * @param The type of the second parameter + * @see Functor + * @see Profunctor + * @see com.jnape.palatable.lambda.adt.tuples.Tuple2 + */ +@FunctionalInterface +public interface Bifunctor { + + default Bifunctor biMapL(MonadicFunction fn) { + return biMap(fn, id()); + } + + default Bifunctor biMapR(MonadicFunction fn) { + return biMap(id(), fn); + } + + Bifunctor biMap(MonadicFunction f1, + MonadicFunction f2); +} diff --git a/src/main/java/com/jnape/palatable/lambda/functor/Functor.java b/src/main/java/com/jnape/palatable/lambda/functor/Functor.java new file mode 100644 index 000000000..1a411061e --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functor/Functor.java @@ -0,0 +1,23 @@ +package com.jnape.palatable.lambda.functor; + +import com.jnape.palatable.lambda.functions.MonadicFunction; + +/** + * An interface for the generic covariant functorial operation map over some parameter A. + * Functors are foundational to many of the classes provided by this library; generally, anything that can be thought of + * as "mappable" is an instance of at least this interface. + *

+ * For more information, read about Functors. + * + * @param The type of the parameter + * @see Bifunctor + * @see Profunctor + * @see MonadicFunction + * @see com.jnape.palatable.lambda.adt.tuples.Tuple2 + * @see com.jnape.palatable.lambda.adt.Either + */ +@FunctionalInterface +public interface Functor { + + Functor fmap(MonadicFunction fn); +} diff --git a/src/main/java/com/jnape/palatable/lambda/functor/Profunctor.java b/src/main/java/com/jnape/palatable/lambda/functor/Profunctor.java new file mode 100644 index 000000000..edc79ab45 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functor/Profunctor.java @@ -0,0 +1,32 @@ +package com.jnape.palatable.lambda.functor; + +import com.jnape.palatable.lambda.functions.MonadicFunction; + +import static com.jnape.palatable.lambda.functions.builtin.monadic.Identity.id; + +/** + * A dually-parametric Functor that maps contravariantly over the first parameter and covariantly over the + * second. + *

+ * For more information, read about Profunctors. + * + * @param The type of the first parameter + * @param The type of the second parameter + * @see Functor + * @see Bifunctor + * @see com.jnape.palatable.lambda.functions.DyadicFunction + */ +@FunctionalInterface +public interface Profunctor { + + default Profunctor diMapL(MonadicFunction fn) { + return diMap(fn, id()); + } + + default Profunctor diMapR(MonadicFunction fn) { + return diMap(id(), fn); + } + + Profunctor diMap(MonadicFunction f1, + MonadicFunction f2); +} diff --git a/src/main/java/com/jnape/palatable/lambda/iterators/PredicatedTakingIterator.java b/src/main/java/com/jnape/palatable/lambda/iterators/PredicatedTakingIterator.java index 1dbd5c29b..eb9bc32a8 100644 --- a/src/main/java/com/jnape/palatable/lambda/iterators/PredicatedTakingIterator.java +++ b/src/main/java/com/jnape/palatable/lambda/iterators/PredicatedTakingIterator.java @@ -7,7 +7,7 @@ public class PredicatedTakingIterator extends ImmutableIterator { private final MonadicFunction predicate; - private final RewindableIterator rewindableIterator; + private final RewindableIterator rewindableIterator; private boolean stillTaking; public PredicatedTakingIterator(MonadicFunction predicate, diff --git a/src/main/java/com/jnape/palatable/lambda/iterators/RewindableIterator.java b/src/main/java/com/jnape/palatable/lambda/iterators/RewindableIterator.java index e9a5c616f..67529fbac 100644 --- a/src/main/java/com/jnape/palatable/lambda/iterators/RewindableIterator.java +++ b/src/main/java/com/jnape/palatable/lambda/iterators/RewindableIterator.java @@ -5,36 +5,9 @@ public class RewindableIterator extends ImmutableIterator { - private static class Cache { - - private A cache; - - public void store(A a) { - cache = a; - } - - public A retrieve() { - if (cache == null) - throw new NoSuchElementException("Cache is empty."); - - A cache = this.cache; - this.cache = null; - return cache; - } - - public boolean isEmpty() { - return cache == null; - } - - public boolean isNotEmpty() { - return !isEmpty(); - } - } - private final Iterator asIterator; private final Cache cache; private boolean rewound; - public RewindableIterator(Iterator asIterator) { this.asIterator = asIterator; cache = new Cache<>(); @@ -67,4 +40,30 @@ public void rewind() { public boolean isRewound() { return rewound; } + + private static class Cache { + + private A cache; + + public void store(A a) { + cache = a; + } + + public A retrieve() { + if (cache == null) + throw new NoSuchElementException("Cache is empty."); + + A cache = this.cache; + this.cache = null; + return cache; + } + + public boolean isEmpty() { + return cache == null; + } + + public boolean isNotEmpty() { + return !isEmpty(); + } + } } diff --git a/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationIteratorTest.java b/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationIteratorTest.java deleted file mode 100644 index 829193af8..000000000 --- a/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationIteratorTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.jnape.palatable.lambda.continuation; - -import org.junit.Test; - -import java.util.NoSuchElementException; -import java.util.Optional; - -import static com.jnape.palatable.lambda.continuation.Continuations.continuing; -import static org.hamcrest.MatcherAssert.assertThat; -import static testsupport.matchers.IterableMatcher.iterates; - -public class ContinuationIteratorTest { - - @Test - public void iteratesContinuation() { - Continuation numbers = continuing(1, 2, 3); - ContinuationIterator continuationIterator = new ContinuationIterator<>(numbers); - - assertThat(() -> continuationIterator, iterates(1, 2, 3)); - } - - @Test(expected = NoSuchElementException.class) - public void usesStandardSemanticFailuresIfIteratingPastLastContinuation() { - new ContinuationIterator(Optional::empty).next(); - } -} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationTest.java b/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationTest.java deleted file mode 100644 index ce7bb4aa0..000000000 --- a/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.jnape.palatable.lambda.continuation; - -import org.junit.Test; - -import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; - -import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; -import static com.jnape.palatable.lambda.continuation.Continuations.continuing; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static testsupport.matchers.IterableMatcher.iterates; - -public class ContinuationTest { - - private static final Continuation ONE_TWO_THREE = continuing(1, 2, 3); - - @Test - public void isIterable() { - assertThat(ONE_TWO_THREE, iterates(1, 2, 3)); - } - - @Test - public void fmapRecursivelyComposesContinuationLineage() { - assertThat(ONE_TWO_THREE.fmap(Object::toString), iterates("1", "2", "3")); - } - - @Test - public void thenContinuesOnToNextContinuationOnceEmpty() { - Continuation fourFiveSix = continuing(4, 5, 6); - assertThat(ONE_TWO_THREE.then(fourFiveSix), iterates(1, 2, 3, 4, 5, 6)); - } - - @Test - public void memoizesEveryResult() { - AtomicInteger index = new AtomicInteger(0); - - Continuation evilSideEffectDrivenContinuation = () -> - Optional.of(tuple(index.incrementAndGet(), () -> - Optional.of(tuple(index.incrementAndGet(), Optional::empty)))); - - Continuation memoizingContinuation = evilSideEffectDrivenContinuation.memoized(); - - assertThat(memoizingContinuation, iterates(1, 2)); - assertThat(memoizingContinuation, iterates(1, 2)); - assertThat(memoizingContinuation, iterates(1, 2)); - } - - @Test - public void memoizationIsLazy() { - AtomicInteger invocations = new AtomicInteger(0); - Continuation invocationCountingContinuation = continuing(1, 2, 3, 4, 5).fmap(i -> { - invocations.incrementAndGet(); - return i; - }); - - invocationCountingContinuation.memoized(); - - assertThat(invocations.get(), is(0)); - } -} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationsTest.java b/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationsTest.java deleted file mode 100644 index 6229b2f6d..000000000 --- a/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationsTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.jnape.palatable.lambda.continuation; - -import org.junit.Test; - -import java.lang.reflect.Constructor; -import java.util.stream.Stream; - -import static com.jnape.palatable.lambda.continuation.Continuations.continuing; -import static java.lang.reflect.Modifier.isPrivate; -import static java.util.Arrays.asList; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static testsupport.matchers.IterableMatcher.iterates; - -public class ContinuationsTest { - - @Test - public void cannotBeInstantiated() { - Constructor[] constructors = Continuations.class.getDeclaredConstructors(); - assertThat(constructors.length, is(1)); - assertTrue(isPrivate(constructors[0].getModifiers())); - } - - @Test - public void continuingVarargs() { - assertThat(continuing(1, 2, 3), iterates(1, 2, 3)); - } - - @Test - public void continuingStreams() { - Stream stream = Stream.builder() - .add(1) - .add(2) - .add(3) - .build(); - assertThat(continuing(stream), iterates(1, 2, 3)); - } - - @Test - public void continuingIterables() { - assertThat(continuing(asList(1, 2, 3)), iterates(1, 2, 3)); - } - - @Test - public void continuingIterators() { - assertThat(continuing(asList(1, 2, 3).iterator()), iterates(1, 2, 3)); - } -} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/continuation/MemoTest.java b/src/test/java/com/jnape/palatable/lambda/continuation/MemoTest.java deleted file mode 100644 index 4d3490bf0..000000000 --- a/src/test/java/com/jnape/palatable/lambda/continuation/MemoTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.jnape.palatable.lambda.continuation; - -import org.junit.Test; -import testsupport.concurrent.Turnstile; - -import java.util.Random; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Supplier; - -import static com.jnape.palatable.lambda.continuation.Memo.memoize; -import static java.lang.Thread.sleep; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; - -public class MemoTest { - - @Test - public void computesValueOnlyOnce() { - AtomicInteger counter = new AtomicInteger(0); - Memo memo = memoize(counter::incrementAndGet); - - assertThat(memo.get(), is(1)); - assertThat(memo.get(), is(1)); - assertThat(memo.get(), is(1)); - assertThat(memo.get(), is(1)); - assertThat(memo.get(), is(1)); - } - - @Test - public void memoizesValuesToo() { - Memo memo = memoize(2); - assertThat(memo.get(), is(2)); - } - - @Test - public void onlyMemoizesValueOfWinningThreadInMultiThreadedRaceSituation() throws InterruptedException { - Turnstile turnstile = new Turnstile(2); - - Supplier randomIntSupplier = () -> { - int i = new Random().nextInt(); - turnstile.arrive(); - return i; - }; - - Memo memo = memoize(randomIntSupplier); - - AtomicInteger thread1Value = new AtomicInteger(); - AtomicInteger thread2Value = new AtomicInteger(); - - Thread thread1 = new Thread(() -> thread1Value.set(memo.get())); - Thread thread2 = new Thread(() -> thread2Value.set(memo.get())); - - thread1.start(); - sleep(100); - thread2.start(); - - thread2.join(); - - assertThat(memo.get(), is(thread1Value.get())); - } -} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/exceptions/EmptyIterableExceptionTest.java b/src/test/java/com/jnape/palatable/lambda/exceptions/EmptyIterableExceptionTest.java deleted file mode 100644 index 657f63bcf..000000000 --- a/src/test/java/com/jnape/palatable/lambda/exceptions/EmptyIterableExceptionTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.jnape.palatable.lambda.exceptions; - -import org.junit.Test; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - -public class EmptyIterableExceptionTest { - - @Test - public void betterNotBeChecked() { - assertThat(RuntimeException.class.isInstance(new EmptyIterableException()), is(true)); - } - - @Test - public void hasUsefulMessage() { - assertThat(new EmptyIterableException().getMessage(), is("Iterable was empty.")); - } -} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/AllTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/AllTest.java index 182755da3..993e4d0a2 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/AllTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/AllTest.java @@ -8,7 +8,7 @@ import testsupport.traits.EmptyIterableSupport; import static com.jnape.palatable.lambda.functions.builtin.dyadic.All.all; -import static com.jnape.palatable.lambda.functions.builtin.monadic.Always.always; +import static com.jnape.palatable.lambda.functions.builtin.monadic.Constantly.constantly; import static com.jnape.palatable.lambda.functions.builtin.monadic.Repeat.repeat; import static java.util.Arrays.asList; import static org.hamcrest.core.Is.is; @@ -21,7 +21,7 @@ public class AllTest { @TestTraits({EmptyIterableSupport.class}) public MonadicFunction, Boolean> createTestSubject() { - return all(always(true)); + return all(constantly(true)); } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/AnyTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/AnyTest.java index 65fd70de5..6ace0734a 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/AnyTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/AnyTest.java @@ -8,7 +8,7 @@ import testsupport.traits.EmptyIterableSupport; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Any.any; -import static com.jnape.palatable.lambda.functions.builtin.monadic.Always.always; +import static com.jnape.palatable.lambda.functions.builtin.monadic.Constantly.constantly; import static com.jnape.palatable.lambda.functions.builtin.monadic.Repeat.repeat; import static java.util.Arrays.asList; import static org.hamcrest.core.Is.is; @@ -21,7 +21,7 @@ public class AnyTest { @TestTraits({EmptyIterableSupport.class}) public MonadicFunction, Boolean> createTestSubject() { - return any(always(true)); + return any(constantly(true)); } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhileTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhileTest.java index 54807f077..a729ce240 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhileTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhileTest.java @@ -12,7 +12,7 @@ import testsupport.traits.Laziness; import static com.jnape.palatable.lambda.functions.builtin.dyadic.DropWhile.dropWhile; -import static com.jnape.palatable.lambda.functions.builtin.monadic.Always.always; +import static com.jnape.palatable.lambda.functions.builtin.monadic.Constantly.constantly; import static java.util.Arrays.asList; import static org.junit.Assert.assertThat; import static testsupport.matchers.IterableMatcher.isEmpty; @@ -23,7 +23,7 @@ public class DropWhileTest { @TestTraits({Laziness.class, ImmutableIteration.class, FiniteIteration.class, EmptyIterableSupport.class}) public MonadicFunction, Iterable> createTestSubject() { - return dropWhile(always(true)); + return dropWhile(constantly(true)); } @Test @@ -34,11 +34,11 @@ public void dropsElementsWhilePredicateIsTrue() { @Test public void dropsAllElementsIfPredicateNeverFails() { - assertThat(dropWhile(always(true), asList(1, 2, 3)), isEmpty()); + assertThat(dropWhile(constantly(true), asList(1, 2, 3)), isEmpty()); } @Test public void dropsNoElementsIfPredicateImmediatelyFails() { - assertThat(dropWhile(always(false), asList(1, 2, 3)), iterates(1, 2, 3)); + assertThat(dropWhile(constantly(false), asList(1, 2, 3)), iterates(1, 2, 3)); } } diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/FilterTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/FilterTest.java index 3ad02a9de..bd534cd0c 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/FilterTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/FilterTest.java @@ -12,7 +12,7 @@ import testsupport.traits.Laziness; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Filter.filter; -import static com.jnape.palatable.lambda.functions.builtin.monadic.Always.always; +import static com.jnape.palatable.lambda.functions.builtin.monadic.Constantly.constantly; import static java.util.Arrays.asList; import static org.junit.Assert.assertThat; import static testsupport.matchers.IterableMatcher.iterates; @@ -22,7 +22,7 @@ public class FilterTest { @TestTraits({Laziness.class, EmptyIterableSupport.class, FiniteIteration.class, ImmutableIteration.class}) public MonadicFunction createTraitsTestSubject() { - return filter(always(true)); + return filter(constantly(true)); } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/IterateTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/IterateTest.java index 9f721018b..984794010 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/IterateTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/IterateTest.java @@ -13,7 +13,7 @@ import static com.jnape.palatable.lambda.functions.builtin.dyadic.Iterate.iterate; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Take.take; -import static com.jnape.palatable.lambda.functions.builtin.monadic.Always.always; +import static com.jnape.palatable.lambda.functions.builtin.monadic.Constantly.constantly; import static org.junit.Assert.assertThat; import static testsupport.matchers.IterableMatcher.iterates; @@ -22,7 +22,7 @@ public class IterateTest { @TestTraits({Laziness.class, InfiniteIteration.class, ImmutableIteration.class}) public MonadicFunction createTestSubject() { - return iterate(always(new ArrayList<>())); + return iterate(constantly(new ArrayList<>())); } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeftTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeftTest.java index 1b809f249..53d0bdcd5 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeftTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeftTest.java @@ -1,12 +1,12 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; -import com.jnape.palatable.lambda.exceptions.EmptyIterableException; import org.junit.Test; -import java.util.Collections; +import java.util.Optional; import static com.jnape.palatable.lambda.functions.builtin.dyadic.ReduceLeft.reduceLeft; import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static testsupport.functions.ExplainFold.explainFold; @@ -17,12 +17,15 @@ public class ReduceLeftTest { public void reduceLeftAccumulatesLeftToRightUsingFirstElementAsStartingAccumulation() { assertThat( reduceLeft(explainFold(), asList("1", "2", "3", "4", "5")), - is("((((1 + 2) + 3) + 4) + 5)") + is(Optional.of("((((1 + 2) + 3) + 4) + 5)")) ); } - @Test(expected = EmptyIterableException.class) - public void reduceLeftFailsIfEmptyIterable() { - reduceLeft(explainFold(), Collections.emptyList()); + @Test + public void isEmptyIfIterableIsEmpty() { + assertThat( + reduceLeft(explainFold(), emptyList()), + is(Optional.empty()) + ); } } diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceRightTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceRightTest.java index 3103d2694..1f2e0032f 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceRightTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceRightTest.java @@ -2,8 +2,11 @@ import org.junit.Test; +import java.util.Optional; + import static com.jnape.palatable.lambda.functions.builtin.dyadic.ReduceRight.reduceRight; import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static testsupport.functions.ExplainFold.explainFold; @@ -11,10 +14,18 @@ public class ReduceRightTest { @Test - public void reduceRightAccumulatesRightToLeftUsingLastElementAsStartingAccumulation() { + public void accumulatesRightToLeftUsingLastElementAsStartingAccumulation() { assertThat( reduceRight(explainFold(), asList("1", "2", "3", "4", "5")), - is("(1 + (2 + (3 + (4 + 5))))") + is(Optional.of("(1 + (2 + (3 + (4 + 5))))")) + ); + } + + @Test + public void isEmptyIfIterableIsEmpty() { + assertThat( + reduceRight(explainFold(), emptyList()), + is(Optional.empty()) ); } } diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhileTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhileTest.java index 9025adf66..ed75773c0 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhileTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhileTest.java @@ -12,7 +12,7 @@ import testsupport.traits.Laziness; import static com.jnape.palatable.lambda.functions.builtin.dyadic.TakeWhile.takeWhile; -import static com.jnape.palatable.lambda.functions.builtin.monadic.Always.always; +import static com.jnape.palatable.lambda.functions.builtin.monadic.Constantly.constantly; import static java.util.Arrays.asList; import static org.junit.Assert.assertThat; import static testsupport.matchers.IterableMatcher.isEmpty; @@ -23,7 +23,7 @@ public class TakeWhileTest { @TestTraits({FiniteIteration.class, EmptyIterableSupport.class, ImmutableIteration.class, Laziness.class}) public MonadicFunction, Iterable> createTestObject() { - return takeWhile(always(true)); + return takeWhile(constantly(true)); } @Test @@ -37,13 +37,13 @@ public void takesElementsWhilePredicateIsTrue() { public void takesAllElementsIfPredicateNeverFails() { String[] requirements = {"fast", "good", "cheap"}; assertThat( - takeWhile(always(true), asList(requirements)), + takeWhile(constantly(true), asList(requirements)), iterates(requirements) ); } @Test public void takesNoElementsIfPredicateImmediatelyFails() { - assertThat(takeWhile(always(false), asList(1, 2, 3)), isEmpty()); + assertThat(takeWhile(constantly(false), asList(1, 2, 3)), isEmpty()); } } diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/AlwaysTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/ConstantlyTest.java similarity index 74% rename from src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/AlwaysTest.java rename to src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/ConstantlyTest.java index 319807b2e..0543c48c3 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/AlwaysTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/ConstantlyTest.java @@ -2,15 +2,15 @@ import org.junit.Test; -import static com.jnape.palatable.lambda.functions.builtin.monadic.Always.always; +import static com.jnape.palatable.lambda.functions.builtin.monadic.Constantly.constantly; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; -public class AlwaysTest { +public class ConstantlyTest { @Test public void returnsConstantOutputRegardlessOfInput() { Object anything = "doesn't matter"; - assertThat(always("yes").apply(anything), is("yes")); + assertThat(constantly("yes").apply(anything), is("yes")); } } diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/ReverseTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/ReverseTest.java index fb289bc6c..53d0e58f2 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/ReverseTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/ReverseTest.java @@ -14,7 +14,10 @@ import static com.jnape.palatable.lambda.functions.builtin.monadic.Reverse.reverse; import static java.util.Arrays.asList; import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import static testsupport.matchers.IterableMatcher.iterates; @RunWith(Traits.class) diff --git a/src/test/java/com/jnape/palatable/lambda/functions/specialized/CheckedTest.java b/src/test/java/com/jnape/palatable/lambda/functions/specialized/CheckedTest.java deleted file mode 100644 index df6f9c388..000000000 --- a/src/test/java/com/jnape/palatable/lambda/functions/specialized/CheckedTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.jnape.palatable.lambda.functions.specialized; - -import com.jnape.palatable.lambda.functions.specialized.unchecked.CheckedMonadicFunction; -import com.jnape.palatable.lambda.functions.specialized.unchecked.CheckedSupplier; -import org.junit.Test; - -import static com.jnape.palatable.lambda.functions.specialized.Checked.checked; - - -public class CheckedTest { - - @Test(expected = IllegalStateException.class) - public void checkedSupplier() { - CheckedSupplier supplier = checked(() -> { - throw new IllegalStateException("expected"); - }); - supplier.get(); - } - - @Test(expected = IllegalStateException.class) - public void uncheckedMonadicFunction() { - CheckedMonadicFunction fn = checked(x -> { - throw new IllegalStateException("expected"); - }); - fn.apply("foo"); - } -} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/applicative/BiFunctorTest.java b/src/test/java/com/jnape/palatable/lambda/functor/BifunctorTest.java similarity index 60% rename from src/test/java/com/jnape/palatable/lambda/applicative/BiFunctorTest.java rename to src/test/java/com/jnape/palatable/lambda/functor/BifunctorTest.java index 9e23cd320..d5b779e6d 100644 --- a/src/test/java/com/jnape/palatable/lambda/applicative/BiFunctorTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functor/BifunctorTest.java @@ -1,8 +1,8 @@ -package com.jnape.palatable.lambda.applicative; +package com.jnape.palatable.lambda.functor; import com.jnape.palatable.lambda.functions.MonadicFunction; import org.junit.Test; -import testsupport.applicatives.InvocationRecordingBiFunctor; +import testsupport.applicatives.InvocationRecordingBifunctor; import java.util.concurrent.atomic.AtomicReference; @@ -10,21 +10,21 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; -public class BiFunctorTest { +public class BifunctorTest { @Test public void biMapLUsesIdentityForRightBiMapFunction() { AtomicReference rightInvocation = new AtomicReference<>(); - BiFunctor biFunctor = new InvocationRecordingBiFunctor<>(new AtomicReference<>(), rightInvocation); - biFunctor.biMapL(String::toUpperCase); + Bifunctor bifunctor = new InvocationRecordingBifunctor<>(new AtomicReference<>(), rightInvocation); + bifunctor.biMapL(String::toUpperCase); assertThat(rightInvocation.get(), is(id())); } @Test public void biMapRUsesIdentityForLeftBiMapFunction() { AtomicReference leftInvocation = new AtomicReference<>(); - BiFunctor biFunctor = new InvocationRecordingBiFunctor<>(leftInvocation, new AtomicReference<>()); - biFunctor.biMapR(String::valueOf); + Bifunctor bifunctor = new InvocationRecordingBifunctor<>(leftInvocation, new AtomicReference<>()); + bifunctor.biMapR(String::valueOf); assertThat(leftInvocation.get(), is(id())); } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/applicative/ProFunctorTest.java b/src/test/java/com/jnape/palatable/lambda/functor/ProfunctorTest.java similarity index 60% rename from src/test/java/com/jnape/palatable/lambda/applicative/ProFunctorTest.java rename to src/test/java/com/jnape/palatable/lambda/functor/ProfunctorTest.java index 948521446..d99fb1478 100644 --- a/src/test/java/com/jnape/palatable/lambda/applicative/ProFunctorTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functor/ProfunctorTest.java @@ -1,8 +1,8 @@ -package com.jnape.palatable.lambda.applicative; +package com.jnape.palatable.lambda.functor; import com.jnape.palatable.lambda.functions.MonadicFunction; import org.junit.Test; -import testsupport.applicatives.InvocationRecordingProFunctor; +import testsupport.applicatives.InvocationRecordingProfunctor; import java.util.concurrent.atomic.AtomicReference; @@ -10,21 +10,21 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; -public class ProFunctorTest { +public class ProfunctorTest { @Test public void diMapLUsesIdentityForRightDiMapFunction() { AtomicReference rightInvocation = new AtomicReference<>(); - ProFunctor proFunctor = new InvocationRecordingProFunctor<>(new AtomicReference<>(), rightInvocation); - proFunctor.diMapL(Object::toString); + Profunctor profunctor = new InvocationRecordingProfunctor<>(new AtomicReference<>(), rightInvocation); + profunctor.diMapL(Object::toString); assertThat(rightInvocation.get(), is(id())); } @Test public void diMapRUsesIdentityForLeftDiMapFunction() { AtomicReference leftInvocation = new AtomicReference<>(); - ProFunctor proFunctor = new InvocationRecordingProFunctor<>(leftInvocation, new AtomicReference<>()); - proFunctor.diMapR(String::valueOf); + Profunctor profunctor = new InvocationRecordingProfunctor<>(leftInvocation, new AtomicReference<>()); + profunctor.diMapR(String::valueOf); assertThat(leftInvocation.get(), is(id())); } } \ No newline at end of file diff --git a/src/test/java/testsupport/applicatives/InvocationRecordingBiFunctor.java b/src/test/java/testsupport/applicatives/InvocationRecordingBifunctor.java similarity index 67% rename from src/test/java/testsupport/applicatives/InvocationRecordingBiFunctor.java rename to src/test/java/testsupport/applicatives/InvocationRecordingBifunctor.java index e0e09090b..86fa80278 100644 --- a/src/test/java/testsupport/applicatives/InvocationRecordingBiFunctor.java +++ b/src/test/java/testsupport/applicatives/InvocationRecordingBifunctor.java @@ -1,15 +1,15 @@ package testsupport.applicatives; -import com.jnape.palatable.lambda.applicative.BiFunctor; import com.jnape.palatable.lambda.functions.MonadicFunction; +import com.jnape.palatable.lambda.functor.Bifunctor; import java.util.concurrent.atomic.AtomicReference; -public final class InvocationRecordingBiFunctor implements BiFunctor { +public final class InvocationRecordingBifunctor implements Bifunctor { private final AtomicReference leftFn; private final AtomicReference rightFn; - public InvocationRecordingBiFunctor(AtomicReference leftFn, + public InvocationRecordingBifunctor(AtomicReference leftFn, AtomicReference rightFn) { this.leftFn = leftFn; this.rightFn = rightFn; @@ -17,10 +17,10 @@ public InvocationRecordingBiFunctor(AtomicReference leftFn, @Override @SuppressWarnings("unchecked") - public BiFunctor biMap(MonadicFunction f1, + public Bifunctor biMap(MonadicFunction f1, MonadicFunction f2) { leftFn.set(f1); rightFn.set(f2); - return (BiFunctor) this; + return (Bifunctor) this; } } diff --git a/src/test/java/testsupport/applicatives/InvocationRecordingProFunctor.java b/src/test/java/testsupport/applicatives/InvocationRecordingProfunctor.java similarity index 67% rename from src/test/java/testsupport/applicatives/InvocationRecordingProFunctor.java rename to src/test/java/testsupport/applicatives/InvocationRecordingProfunctor.java index deef7c403..7cb8bef0d 100644 --- a/src/test/java/testsupport/applicatives/InvocationRecordingProFunctor.java +++ b/src/test/java/testsupport/applicatives/InvocationRecordingProfunctor.java @@ -1,15 +1,15 @@ package testsupport.applicatives; -import com.jnape.palatable.lambda.applicative.ProFunctor; import com.jnape.palatable.lambda.functions.MonadicFunction; +import com.jnape.palatable.lambda.functor.Profunctor; import java.util.concurrent.atomic.AtomicReference; -public final class InvocationRecordingProFunctor implements ProFunctor { +public final class InvocationRecordingProfunctor implements Profunctor { private final AtomicReference leftFn; private final AtomicReference rightFn; - public InvocationRecordingProFunctor(AtomicReference leftFn, + public InvocationRecordingProfunctor(AtomicReference leftFn, AtomicReference rightFn) { this.leftFn = leftFn; this.rightFn = rightFn; @@ -17,10 +17,10 @@ public InvocationRecordingProFunctor(AtomicReference leftFn, @Override @SuppressWarnings("unchecked") - public ProFunctor diMap(MonadicFunction f1, + public Profunctor diMap(MonadicFunction f1, MonadicFunction f2) { leftFn.set(f1); rightFn.set(f2); - return (ProFunctor) this; + return (Profunctor) this; } } From a0783358eb2f9fa5cf2cde5e731ddcb5e3e956d5 Mon Sep 17 00:00:00 2001 From: jnape Date: Tue, 21 Jun 2016 00:05:18 -0500 Subject: [PATCH 18/18] [maven-release-plugin] prepare release lambda-1.1 --- pom.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index ad3aaba01..fac9872d1 100644 --- a/pom.xml +++ b/pom.xml @@ -1,6 +1,5 @@ - + 4.0.0 @@ -10,7 +9,7 @@ lambda - 1.1-SNAPSHOT + 1.1 jar Lambda