From f2ddb4002d95a42317288f8fd1801c918782c01a Mon Sep 17 00:00:00 2001 From: jnape Date: Mon, 13 Nov 2017 01:03:42 -0600 Subject: [PATCH 01/24] [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 b395baaa9..357cc5c06 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ lambda - 2.0.0 + 2.0.1-SNAPSHOT jar Lambda From f3e0f9f6efa55a7502f3e653f17d7dbbabde39a3 Mon Sep 17 00:00:00 2001 From: jnape Date: Mon, 13 Nov 2017 01:07:10 -0600 Subject: [PATCH 02/24] Updating README and CHANGELOG per 2.0.0 release; moving changed to top of changelog entries --- CHANGELOG.md | 77 +++++++++++++++++++++++++++------------------------- README.md | 4 +-- 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8871c8c6..e98b54d00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] + +## [2.0.0] - 2017-11-13 +### Changed +- ***Breaking Change***: `java.util.Optional` replaced with `Maybe` across the board +- `Profunctor#diMap/L/R` parameters allow variance +- `Either#toOptional` no longer allows `null` values in the right side, and is now in sync with CoProduct#projectB +- `Unfoldr` allows variance on input + ### Fixed - `CoProductN#embed` no longer eagerly invokes functions - `PrependAll` now only creates `O(1)` `Iterable`s instead of `O(3n + 1)` @@ -45,13 +53,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `Sequence` overloads supporting `Optional` in favor of converting `Optional` to `Maybe` and then sequencing - `Either#toOptional` and `Either#fromOptional` in favor of its `Maybe` counterparts +## [1.6.3] - 2017-09-27 ### Changed -- ***Breaking Change***: `java.util.Optional` replaced with `Maybe` across the board -- `Profunctor#diMap/L/R` parameters allow variance -- `Either#toOptional` no longer allows `null` values in the right side, and is now in sync with CoProduct#projectB -- `Unfoldr` allows variance on input +- Loosening variance on `Fn2#fn2` and `Fn1#fn1` -## [1.6.3] - 2017-09-27 ### Fixed - `ConcatenatingIterator` bug where deeply nested `xs` skip elements @@ -60,9 +65,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `Fn1#adapt` in favor of `Fn1#fn1` (rename) - `Fn2#adapt` in favor of `Fn2#fn2` (rename) -### Changed -- Loosening variance on `Fn2#fn2` and `Fn1#fn1` - ### Added - `Fn1#andThen` overload to support composition with `Bifunction` - `Fn1#compose` overload to support composition with `Bifunction` and `Fn2` @@ -75,6 +77,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `Xor`, a monoid representing logical exclusive-or ## [1.6.2] - 2017-08-20 +### Changed +- Removing need for various suppressed unchecked warnings in `ChoiceN` types +- `HList` abstract super type loses both unnecessary parameters + ### Fixed - ClassCastException `BiPredicate.flip` @@ -83,15 +89,20 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `Compose` semigroup and monoid formed over `CompletableFuture` - `Monoid` and `Semigroup` both preserve type specificity through `flip` calls -### Changed -- Removing need for various suppressed unchecked warnings in `ChoiceN` types -- `HList` abstract super type loses both unnecessary parameters - ## [1.6.1] - 2017-06-17 ### Changed - Loosening visibility on `Traversables` methods to `public` ## [1.6.0] - 2017-06-04 +### Changed +- `Functor`, `Bifunctor`, and `Profunctor` (as well as all instances) get a unification parameter +- `Identity` supports value equality +- `Const` supports value equality +- `partition` now only requires iterables of `CoProudct2` +- `CoProductN`s receive a unification parameter, which trickles down to `Either` and `Choice`s +- `Concat` now represents a monoid for `Iterable`; previous `Concat` semigroup and monoid renamed to more appropriate `AddAll` +- `Lens` is now an instance of `Profunctor` + ### Added - `Either#invert` is pulled up into `CoProduct2` and additionally specialized for `Choice2` - `CoProductN#embed` @@ -104,16 +115,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `sequence` for wrapping a traversable in an applicative during traversal - `Compose`, an applicative functor that represents type-level functor composition +## [1.5.6] - 2017-02-11 ### Changed -- `Functor`, `Bifunctor`, and `Profunctor` (as well as all instances) get a unification parameter -- `Identity` supports value equality -- `Const` supports value equality -- `partition` now only requires iterables of `CoProudct2` -- `CoProductN`s receive a unification parameter, which trickles down to `Either` and `Choice`s -- `Concat` now represents a monoid for `Iterable`; previous `Concat` semigroup and monoid renamed to more appropriate `AddAll` -- `Lens` is now an instance of `Profunctor` +- `CoProductN.[a-e]()` static factory methods moved to equivalent `ChoiceN` class. Coproduct interfaces now solely represent methods, no longer have anonymous implementations, and no longer require a `Functor` constraint -## [1.5.6] - 2017-02-11 ### Added - `ChoiceN` types, representing concrete coproduct implementations that are also `Functor` and `BiFunctor` - `toMap`, `last`, `cons`, `prependAll`, `intersperse` @@ -121,18 +126,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `First` and `Last` monoids over `Optional` - `And` and `Or` monoids over `Boolean` +## [1.5.5] - 2016-12-17 ### Changed -- `CoProductN.[a-e]()` static factory methods moved to equivalent `ChoiceN` class. Coproduct interfaces now solely represent methods, no longer have anonymous implementations, and no longer require a `Functor` constraint +- semigroups and monoids moved under `fn2` package -## [1.5.5] - 2016-12-17 ### Added - `CoProductN#project`, to project disjoint union types into tuples of `Optional` values - `CoProductN#converge`, to drop the magnitude of a coproduct down by one type - `toCollection` and `size` -### Changed -- semigroups and monoids moved under `fn2` package - ## [1.5.4] - 2016-11-27 ### Added - `Fn1/2#adapt` to switch between lambda and `java.util.function` types more easily @@ -150,12 +152,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `Either` is now a `CoProduct2` ## [1.5.2] - 2016-09-24 -### Added -- Heterogeneous list indexes arrive via `Index` - ### Changed - `Lens` static factory method renaming +### Added +- Heterogeneous list indexes arrive via `Index` + ## [1.5.1] - 2016-08-30 ### Added - Independent `Lens` parameter mapping via `mapS`, `mapT`, `mapA`, and `mapB` @@ -172,33 +174,33 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - All function input values become `java.util.function` types, and all function output values remain lambda types, for better compatibility ## [1.3] - 2016-07-31 -### Added -- `HList` specializations support random access lookup - ### Changed - `Profunctor` inheritance hierarchy - Renaming `Identity` to `Id` - `Monadic/Dyadic/TriadicFunction` is now `Fn1/2/3` +### Added +- `HList` specializations support random access lookup + ## [1.2] - 2016-06-27 +### Changed +- `Tuple`s moved under `HList` as specialized subtypes + ### Added - `Either#peek` - `HMap`, heterogeneous maps - `Tuple2` is now also a `Map.Entry` +## [1.1] - 2016-06-21 ### Changed -- `Tuple`s moved under `HList` as specialized subtypes +- Better interoperability between lambda and `java.util.function` types -## [1.1] - 2016-06-21 ### Added - `scanLeft` - `HList`, heterogeneous lists - Added up to `Tuple5` - `Either`, specialized coproduct with success/failure semantics -### Changed -- Better interoperability between lambda and `java.util.function` types - ## [1.0] - 2015-12-29 ### Added - Initial implementation of first-class curried functions @@ -208,7 +210,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `Monadic/Dyadic/TriadicFunction`, `Predicate`, `Tuple2`, `Tuple3` - `Functor`, `BiFunctor`, `ProFunctor` -[Unreleased]: https://github.com/palatable/lambda/compare/lambda-1.6.3...HEAD +[Unreleased]: https://github.com/palatable/lambda/compare/lambda-2.0.0...HEAD +[2.0.0]: https://github.com/palatable/lambda/compare/lambda-1.6.3...lambda-2.0.0 [1.6.3]: https://github.com/palatable/lambda/compare/lambda-1.6.2...lambda-1.6.3 [1.6.2]: https://github.com/palatable/lambda/compare/lambda-1.6.1...lambda-1.6.2 [1.6.1]: https://github.com/palatable/lambda/compare/lambda-1.6.0...lambda-1.6.1 diff --git a/README.md b/README.md index b97d83984..5f0e55fce 100644 --- a/README.md +++ b/README.md @@ -56,14 +56,14 @@ Add the following dependency to your: com.jnape.palatable lambda - 1.6.3 + 2.0.0 ``` `build.gradle` ([Gradle](https://docs.gradle.org/current/userguide/dependency_management.html)): ```gradle -compile group: 'com.jnape.palatable', name: 'lambda', version: '1.6.3' +compile group: 'com.jnape.palatable', name: 'lambda', version: '2.0.0' ``` Examples From 57d9fba940dd51b58d9e526d8d13697b135c1b69 Mon Sep 17 00:00:00 2001 From: jnape Date: Sat, 18 Nov 2017 16:03:58 -0600 Subject: [PATCH 03/24] Adding Tuple6 through Tuple8 --- CHANGELOG.md | 2 + README.md | 24 ++- .../palatable/lambda/adt/hlist/HList.java | 80 +++++++ .../palatable/lambda/adt/hlist/Tuple5.java | 6 +- .../palatable/lambda/adt/hlist/Tuple6.java | 178 +++++++++++++++ .../palatable/lambda/adt/hlist/Tuple7.java | 191 ++++++++++++++++ .../palatable/lambda/adt/hlist/Tuple8.java | 204 ++++++++++++++++++ .../lambda/adt/hlist/Tuple6Test.java | 102 +++++++++ .../lambda/adt/hlist/Tuple7Test.java | 105 +++++++++ .../lambda/adt/hlist/Tuple8Test.java | 108 ++++++++++ 10 files changed, 988 insertions(+), 12 deletions(-) create mode 100644 src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java create mode 100644 src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java create mode 100644 src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java create mode 100644 src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java create mode 100644 src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java create mode 100644 src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java diff --git a/CHANGELOG.md b/CHANGELOG.md index e98b54d00..c67077849 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] +### Added +- `Tuple6` through `Tuple8` ## [2.0.0] - 2017-11-13 ### Changed diff --git a/README.md b/README.md index 5f0e55fce..a4e955801 100644 --- a/README.md +++ b/README.md @@ -475,21 +475,24 @@ HNil nil = hList.tail().tail(); One of the primary downsides to using `HList`s in Java is how quickly the type signature grows. -To address this, tuples in lambda are specializations of `HList`s up to 5 elements deep, with added support for index-based accessor methods. +To address this, tuples in lambda are specializations of `HList`s up to 8 elements deep, with added support for index-based accessor methods. ```Java HNil nil = HList.nil(); -SingletonHList singleton = nil.cons(5); -Tuple2 tuple2 = singleton.cons(4); -Tuple3 tuple3 = tuple2.cons(3); -Tuple4 tuple4 = tuple3.cons(2); -Tuple5 tuple5 = tuple4.cons(1); +SingletonHList singleton = nil.cons(8); +Tuple2 tuple2 = singleton.cons(7); +Tuple3 tuple3 = tuple2.cons(6); +Tuple4 tuple4 = tuple3.cons(5); +Tuple5 tuple5 = tuple4.cons(4); +Tuple6 tuple6 = tuple5.cons(3); +Tuple7 tuple7 = tuple6.cons(2); +Tuple8 tuple8 = tuple7.cons(1); -System.out.println(tuple2._1()); // prints 4 -System.out.println(tuple5._5()); // prints 5 +System.out.println(tuple2._1()); // prints 7 +System.out.println(tuple8._8()); // prints 8 ``` -Additionally, `HList` provides convenience static factory methods for directly constructing lists of up to 5 elements: +Additionally, `HList` provides convenience static factory methods for directly constructing lists of up to 8 elements: ```Java SingletonHList singleton = HList.singletonHList(1); @@ -497,6 +500,9 @@ Tuple2 tuple2 = HList.tuple(1, 2); Tuple3 tuple3 = HList.tuple(1, 2, 3); Tuple4 tuple4 = HList.tuple(1, 2, 3, 4); Tuple5 tuple5 = HList.tuple(1, 2, 3, 4, 5); +Tuple6 tuple6 = HList.tuple(1, 2, 3, 4, 5, 6); +Tuple7 tuple7 = HList.tuple(1, 2, 3, 4, 5, 6, 7); +Tuple8 tuple8 = HList.tuple(1, 2, 3, 4, 5, 6, 7, 8); ``` `Index` can be used for type-safe retrieval and updating of elements at specific indexes: diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/HList.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/HList.java index 16b7a3697..bfb0b8f79 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/HList.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/HList.java @@ -12,6 +12,7 @@ * @see Tuple3 * @see Tuple4 * @see Tuple5 + * @see Tuple6 */ public abstract class HList { @@ -148,6 +149,85 @@ public static <_1, _2, _3, _4, _5> Tuple5<_1, _2, _3, _4, _5> tuple(_1 _1, _2 _2 return tuple(_2, _3, _4, _5).cons(_1); } + /** + * Static factory method for creating a 6-element HList. + * + * @param _1 the head element + * @param _2 the second element + * @param _3 the third element + * @param _4 the fourth element + * @param _5 the fifth element + * @param _6 the sixth element + * @param <_1> the head element type + * @param <_2> the second element type + * @param <_3> the third element type + * @param <_4> the fourth element type + * @param <_5> the fifth element type + * @param <_6> the sixth element type + * @return the 6-element HList + * @see Tuple6 + */ + @SuppressWarnings("JavaDoc") + public static <_1, _2, _3, _4, _5, _6> Tuple6<_1, _2, _3, _4, _5, _6> tuple(_1 _1, _2 _2, _3 _3, _4 _4, _5 _5, + _6 _6) { + return tuple(_2, _3, _4, _5, _6).cons(_1); + } + + /** + * Static factory method for creating a 7-element HList. + * + * @param _1 the head element + * @param _2 the second element + * @param _3 the third element + * @param _4 the fourth element + * @param _5 the fifth element + * @param _6 the sixth element + * @param _7 the seventh element + * @param <_1> the head element type + * @param <_2> the second element type + * @param <_3> the third element type + * @param <_4> the fourth element type + * @param <_5> the fifth element type + * @param <_6> the sixth element type + * @param <_7> the seventh element type + * @return the 7-element HList + * @see Tuple7 + */ + @SuppressWarnings("JavaDoc") + public static <_1, _2, _3, _4, _5, _6, _7> Tuple7<_1, _2, _3, _4, _5, _6, _7> tuple(_1 _1, _2 _2, _3 _3, _4 _4, + _5 _5, _6 _6, _7 _7) { + return tuple(_2, _3, _4, _5, _6, _7).cons(_1); + } + + /** + * Static factory method for creating an 8-element HList. + * + * @param _1 the head element + * @param _2 the second element + * @param _3 the third element + * @param _4 the fourth element + * @param _5 the fifth element + * @param _6 the sixth element + * @param _7 the seventh element + * @param _8 the eighth element + * @param <_1> the head element type + * @param <_2> the second element type + * @param <_3> the third element type + * @param <_4> the fourth element type + * @param <_5> the fifth element type + * @param <_6> the sixth element type + * @param <_7> the seventh element type + * @param <_8> the eighth element type + * @return the 8-element HList + * @see Tuple8 + */ + @SuppressWarnings("JavaDoc") + public static <_1, _2, _3, _4, _5, _6, _7, _8> Tuple8<_1, _2, _3, _4, _5, _6, _7, _8> tuple(_1 _1, _2 _2, _3 _3, + _4 _4, _5 _5, _6 _6, + _7 _7, _8 _8) { + return tuple(_2, _3, _4, _5, _6, _7, _8).cons(_1); + } + /** * The consing of a head element to a tail HList. * diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java index 8dd505fa2..ac205abbc 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java @@ -1,9 +1,9 @@ package com.jnape.palatable.lambda.adt.hlist; -import com.jnape.palatable.lambda.monad.Monad; import com.jnape.palatable.lambda.adt.hlist.HList.HCons; import com.jnape.palatable.lambda.functor.Applicative; import com.jnape.palatable.lambda.functor.Bifunctor; +import com.jnape.palatable.lambda.monad.Monad; import com.jnape.palatable.lambda.traversable.Traversable; import java.util.function.Function; @@ -42,8 +42,8 @@ public class Tuple5<_1, _2, _3, _4, _5> extends HCons<_1, Tuple4<_2, _3, _4, _5> } @Override - public <_0> HCons<_0, Tuple5<_1, _2, _3, _4, _5>> cons(_0 _0) { - return new HCons<>(_0, this); + public <_0> Tuple6<_0, _1, _2, _3, _4, _5> cons(_0 _0) { + return new Tuple6<>(_0, this); } /** diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java new file mode 100644 index 000000000..50cfcd016 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java @@ -0,0 +1,178 @@ +package com.jnape.palatable.lambda.adt.hlist; + +import com.jnape.palatable.lambda.adt.hlist.HList.HCons; +import com.jnape.palatable.lambda.functor.Applicative; +import com.jnape.palatable.lambda.functor.Bifunctor; +import com.jnape.palatable.lambda.monad.Monad; +import com.jnape.palatable.lambda.traversable.Traversable; + +import java.util.function.Function; + +import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly; + +/** + * A 6-element tuple product type, implemented as a specialized HList. Supports random access. + * + * @param <_1> The first slot element type + * @param <_2> The second slot element type + * @param <_3> The third slot element type + * @param <_4> The fourth slot element type + * @param <_5> The fifth slot element type + * @param <_6> The sixth slot element type + * @see HList + * @see SingletonHList + * @see Tuple2 + * @see Tuple3 + * @see Tuple4 + * @see Tuple5 + */ +public class Tuple6<_1, _2, _3, _4, _5, _6> extends HCons<_1, Tuple5<_2, _3, _4, _5, _6>> + implements Monad<_6, Tuple6<_1, _2, _3, _4, _5, ?>>, Bifunctor<_5, _6, Tuple6<_1, _2, _3, _4, ?, ?>>, Traversable<_6, Tuple6<_1, _2, _3, _4, _5, ?>> { + private final _1 _1; + private final _2 _2; + private final _3 _3; + private final _4 _4; + private final _5 _5; + private final _6 _6; + + Tuple6(_1 _1, Tuple5<_2, _3, _4, _5, _6> tail) { + super(_1, tail); + this._1 = _1; + _2 = tail._1(); + _3 = tail._2(); + _4 = tail._3(); + _5 = tail._4(); + _6 = tail._5(); + } + + @Override + public <_0> Tuple7<_0, _1, _2, _3, _4, _5, _6> cons(_0 _0) { + return new Tuple7<>(_0, this); + } + + /** + * Retrieve the first (head) element in constant time. + * + * @return the head element + */ + public _1 _1() { + return _1; + } + + /** + * Retrieve the second element in constant time. + * + * @return the second element + */ + public _2 _2() { + return _2; + } + + /** + * Retrieve the third element in constant time. + * + * @return the third element + */ + public _3 _3() { + return _3; + } + + /** + * Retrieve the fourth element in constant time. + * + * @return the fourth element + */ + public _4 _4() { + return _4; + } + + /** + * Retrieve the fifth element in constant time. + * + * @return the fifth element + */ + public _5 _5() { + return _5; + } + + /** + * Retrieve the sixth element in constant time. + * + * @return the sixth element + */ + public _6 _6() { + return _6; + } + + + @Override + public <_6Prime> Tuple6<_1, _2, _3, _4, _5, _6Prime> fmap(Function fn) { + return Monad.super.<_6Prime>fmap(fn).coerce(); + } + + @Override + @SuppressWarnings("unchecked") + public <_5Prime> Tuple6<_1, _2, _3, _4, _5Prime, _6> biMapL(Function fn) { + return (Tuple6<_1, _2, _3, _4, _5Prime, _6>) Bifunctor.super.biMapL(fn); + } + + @Override + @SuppressWarnings("unchecked") + public <_6Prime> Tuple6<_1, _2, _3, _4, _5, _6Prime> biMapR(Function fn) { + return (Tuple6<_1, _2, _3, _4, _5, _6Prime>) Bifunctor.super.biMapR(fn); + } + + @Override + public <_5Prime, _6Prime> Tuple6<_1, _2, _3, _4, _5Prime, _6Prime> biMap( + Function lFn, + Function rFn) { + return new Tuple6<>(_1(), tail().biMap(lFn, rFn)); + } + + @Override + public <_6Prime> Tuple6<_1, _2, _3, _4, _5, _6Prime> pure(_6Prime _6Prime) { + return tuple(_1, _2, _3, _4, _5, _6Prime); + } + + @Override + public <_6Prime> Tuple6<_1, _2, _3, _4, _5, _6Prime> zip( + Applicative, Tuple6<_1, _2, _3, _4, _5, ?>> appFn) { + return Monad.super.zip(appFn).coerce(); + } + + @Override + public <_6Prime> Tuple6<_1, _2, _3, _4, _5, _6Prime> discardL( + Applicative<_6Prime, Tuple6<_1, _2, _3, _4, _5, ?>> appB) { + return Monad.super.discardL(appB).coerce(); + } + + @Override + public <_6Prime> Tuple6<_1, _2, _3, _4, _5, _6> discardR(Applicative<_6Prime, Tuple6<_1, _2, _3, _4, _5, ?>> appB) { + return Monad.super.discardR(appB).coerce(); + } + + @Override + public <_6Prime> Tuple6<_1, _2, _3, _4, _5, _6Prime> flatMap( + Function>> f) { + return pure(f.apply(_6).>coerce()._6()); + } + + @Override + public <_6Prime, App extends Applicative> Applicative, App> traverse( + Function> fn, + Function>, ? extends Applicative>, App>> pure) { + return fn.apply(_6).fmap(_6Prime -> fmap(constantly(_6Prime))); + } + + /** + * Given a value of type A, produced an instance of this tuple with each slot set to that value. + * + * @param a the value to fill the tuple with + * @param the value type + * @return the filled tuple + * @see Tuple2#fill + */ + public static Tuple6 fill(A a) { + return tuple(a, a, a, a, a, a); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java new file mode 100644 index 000000000..6b74d30eb --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java @@ -0,0 +1,191 @@ +package com.jnape.palatable.lambda.adt.hlist; + +import com.jnape.palatable.lambda.adt.hlist.HList.HCons; +import com.jnape.palatable.lambda.functor.Applicative; +import com.jnape.palatable.lambda.functor.Bifunctor; +import com.jnape.palatable.lambda.monad.Monad; +import com.jnape.palatable.lambda.traversable.Traversable; + +import java.util.function.Function; + +import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly; + +/** + * A 7-element tuple product type, implemented as a specialized HList. Supports random access. + * + * @param <_1> The first slot element type + * @param <_2> The second slot element type + * @param <_3> The third slot element type + * @param <_4> The fourth slot element type + * @param <_5> The fifth slot element type + * @param <_6> The sixth slot element type + * @param <_7> The seventh slot element type + * @see HList + * @see SingletonHList + * @see Tuple2 + * @see Tuple3 + * @see Tuple4 + * @see Tuple5 + * @see Tuple6 + */ +public class Tuple7<_1, _2, _3, _4, _5, _6, _7> extends HCons<_1, Tuple6<_2, _3, _4, _5, _6, _7>> + implements Monad<_7, Tuple7<_1, _2, _3, _4, _5, _6, ?>>, Bifunctor<_6, _7, Tuple7<_1, _2, _3, _4, _5, ?, ?>>, Traversable<_7, Tuple7<_1, _2, _3, _4, _5, _6, ?>> { + private final _1 _1; + private final _2 _2; + private final _3 _3; + private final _4 _4; + private final _5 _5; + private final _6 _6; + private final _7 _7; + + Tuple7(_1 _1, Tuple6<_2, _3, _4, _5, _6, _7> tail) { + super(_1, tail); + this._1 = _1; + _2 = tail._1(); + _3 = tail._2(); + _4 = tail._3(); + _5 = tail._4(); + _6 = tail._5(); + _7 = tail._6(); + } + + @Override + public <_0> Tuple8<_0, _1, _2, _3, _4, _5, _6, _7> cons(_0 _0) { + return new Tuple8<>(_0, this); + } + + /** + * Retrieve the first (head) element in constant time. + * + * @return the head element + */ + public _1 _1() { + return _1; + } + + /** + * Retrieve the second element in constant time. + * + * @return the second element + */ + public _2 _2() { + return _2; + } + + /** + * Retrieve the third element in constant time. + * + * @return the third element + */ + public _3 _3() { + return _3; + } + + /** + * Retrieve the fourth element in constant time. + * + * @return the fourth element + */ + public _4 _4() { + return _4; + } + + /** + * Retrieve the fifth element in constant time. + * + * @return the fifth element + */ + public _5 _5() { + return _5; + } + + /** + * Retrieve the sixth element in constant time. + * + * @return the sixth element + */ + public _6 _6() { + return _6; + } + + /** + * Retrieve the seventh element in constant time. + * + * @return the seventh element + */ + public _7 _7() { + return _7; + } + + @Override + public <_7Prime> Tuple7<_1, _2, _3, _4, _5, _6, _7Prime> fmap(Function fn) { + return Monad.super.<_7Prime>fmap(fn).coerce(); + } + + @Override + @SuppressWarnings("unchecked") + public <_6Prime> Tuple7<_1, _2, _3, _4, _5, _6Prime, _7> biMapL(Function fn) { + return (Tuple7<_1, _2, _3, _4, _5, _6Prime, _7>) Bifunctor.super.biMapL(fn); + } + + @Override + @SuppressWarnings("unchecked") + public <_7Prime> Tuple7<_1, _2, _3, _4, _5, _6, _7Prime> biMapR(Function fn) { + return (Tuple7<_1, _2, _3, _4, _5, _6, _7Prime>) Bifunctor.super.biMapR(fn); + } + + @Override + public <_6Prime, _7Prime> Tuple7<_1, _2, _3, _4, _5, _6Prime, _7Prime> biMap( + Function lFn, + Function rFn) { + return new Tuple7<>(_1(), tail().biMap(lFn, rFn)); + } + + @Override + public <_7Prime> Tuple7<_1, _2, _3, _4, _5, _6, _7Prime> pure(_7Prime _7Prime) { + return tuple(_1, _2, _3, _4, _5, _6, _7Prime); + } + + @Override + public <_7Prime> Tuple7<_1, _2, _3, _4, _5, _6, _7Prime> zip( + Applicative, Tuple7<_1, _2, _3, _4, _5, _6, ?>> appFn) { + return Monad.super.zip(appFn).coerce(); + } + + @Override + public <_7Prime> Tuple7<_1, _2, _3, _4, _5, _6, _7Prime> discardL( + Applicative<_7Prime, Tuple7<_1, _2, _3, _4, _5, _6, ?>> appB) { + return Monad.super.discardL(appB).coerce(); + } + + @Override + public <_7Prime> Tuple7<_1, _2, _3, _4, _5, _6, _7> discardR( + Applicative<_7Prime, Tuple7<_1, _2, _3, _4, _5, _6, ?>> appB) { + return Monad.super.discardR(appB).coerce(); + } + + @Override + public <_7Prime> Tuple7<_1, _2, _3, _4, _5, _6, _7Prime> flatMap( + Function>> f) { + return pure(f.apply(_7).>coerce()._7()); + } + + @Override + public <_7Prime, App extends Applicative> Applicative, App> traverse( + Function> fn, + Function>, ? extends Applicative>, App>> pure) { + return fn.apply(_7).fmap(_7Prime -> fmap(constantly(_7Prime))); + } + + /** + * Given a value of type A, produced an instance of this tuple with each slot set to that value. + * + * @param a the value to fill the tuple with + * @param the value type + * @return the filled tuple + * @see Tuple2#fill + */ + public static Tuple7 fill(A a) { + return tuple(a, a, a, a, a, a, a); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java new file mode 100644 index 000000000..8f1b24bef --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java @@ -0,0 +1,204 @@ +package com.jnape.palatable.lambda.adt.hlist; + +import com.jnape.palatable.lambda.adt.hlist.HList.HCons; +import com.jnape.palatable.lambda.functor.Applicative; +import com.jnape.palatable.lambda.functor.Bifunctor; +import com.jnape.palatable.lambda.monad.Monad; +import com.jnape.palatable.lambda.traversable.Traversable; + +import java.util.function.Function; + +import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly; + +/** + * An 8-element tuple product type, implemented as a specialized HList. Supports random access. + * + * @param <_1> The first slot element type + * @param <_2> The second slot element type + * @param <_3> The third slot element type + * @param <_4> The fourth slot element type + * @param <_5> The fifth slot element type + * @param <_6> The sixth slot element type + * @param <_7> The seventh slot element type + * @param <_8> The eighth slot element type + * @see HList + * @see SingletonHList + * @see Tuple2 + * @see Tuple3 + * @see Tuple4 + * @see Tuple5 + * @see Tuple6 + * @see Tuple7 + */ +public class Tuple8<_1, _2, _3, _4, _5, _6, _7, _8> extends HCons<_1, Tuple7<_2, _3, _4, _5, _6, _7, _8>> + implements Monad<_8, Tuple8<_1, _2, _3, _4, _5, _6, _7, ?>>, Bifunctor<_7, _8, Tuple8<_1, _2, _3, _4, _5, _6, ?, ?>>, Traversable<_8, Tuple8<_1, _2, _3, _4, _5, _6, _7, ?>> { + private final _1 _1; + private final _2 _2; + private final _3 _3; + private final _4 _4; + private final _5 _5; + private final _6 _6; + private final _7 _7; + private final _8 _8; + + Tuple8(_1 _1, Tuple7<_2, _3, _4, _5, _6, _7, _8> tail) { + super(_1, tail); + this._1 = _1; + _2 = tail._1(); + _3 = tail._2(); + _4 = tail._3(); + _5 = tail._4(); + _6 = tail._5(); + _7 = tail._6(); + _8 = tail._7(); + } + + @Override + public <_0> HCons<_0, Tuple8<_1, _2, _3, _4, _5, _6, _7, _8>> cons(_0 _0) { + return new HCons<>(_0, this); + } + + /** + * Retrieve the first (head) element in constant time. + * + * @return the head element + */ + public _1 _1() { + return _1; + } + + /** + * Retrieve the second element in constant time. + * + * @return the second element + */ + public _2 _2() { + return _2; + } + + /** + * Retrieve the third element in constant time. + * + * @return the third element + */ + public _3 _3() { + return _3; + } + + /** + * Retrieve the fourth element in constant time. + * + * @return the fourth element + */ + public _4 _4() { + return _4; + } + + /** + * Retrieve the fifth element in constant time. + * + * @return the fifth element + */ + public _5 _5() { + return _5; + } + + /** + * Retrieve the sixth element in constant time. + * + * @return the sixth element + */ + public _6 _6() { + return _6; + } + + /** + * Retrieve the seventh element in constant time. + * + * @return the seventh element + */ + public _7 _7() { + return _7; + } + + /** + * Retrieve the eighth element in constant time. + * + * @return the eighth element + */ + public _8 _8() { + return _8; + } + + @Override + public <_8Prime> Tuple8<_1, _2, _3, _4, _5, _6, _7, _8Prime> fmap(Function fn) { + return Monad.super.<_8Prime>fmap(fn).coerce(); + } + + @Override + @SuppressWarnings("unchecked") + public <_7Prime> Tuple8<_1, _2, _3, _4, _5, _6, _7Prime, _8> biMapL(Function fn) { + return (Tuple8<_1, _2, _3, _4, _5, _6, _7Prime, _8>) Bifunctor.super.biMapL(fn); + } + + @Override + @SuppressWarnings("unchecked") + public <_8Prime> Tuple8<_1, _2, _3, _4, _5, _6, _7, _8Prime> biMapR(Function fn) { + return (Tuple8<_1, _2, _3, _4, _5, _6, _7, _8Prime>) Bifunctor.super.biMapR(fn); + } + + @Override + public <_7Prime, _8Prime> Tuple8<_1, _2, _3, _4, _5, _6, _7Prime, _8Prime> biMap( + Function lFn, + Function rFn) { + return new Tuple8<>(_1(), tail().biMap(lFn, rFn)); + } + + @Override + public <_8Prime> Tuple8<_1, _2, _3, _4, _5, _6, _7, _8Prime> pure(_8Prime _8Prime) { + return tuple(_1, _2, _3, _4, _5, _6, _7, _8Prime); + } + + @Override + public <_8Prime> Tuple8<_1, _2, _3, _4, _5, _6, _7, _8Prime> zip( + Applicative, Tuple8<_1, _2, _3, _4, _5, _6, _7, ?>> appFn) { + return Monad.super.zip(appFn).coerce(); + } + + @Override + public <_8Prime> Tuple8<_1, _2, _3, _4, _5, _6, _7, _8Prime> discardL( + Applicative<_8Prime, Tuple8<_1, _2, _3, _4, _5, _6, _7, ?>> appB) { + return Monad.super.discardL(appB).coerce(); + } + + @Override + public <_8Prime> Tuple8<_1, _2, _3, _4, _5, _6, _7, _8> discardR( + Applicative<_8Prime, Tuple8<_1, _2, _3, _4, _5, _6, _7, ?>> appB) { + return Monad.super.discardR(appB).coerce(); + } + + @Override + public <_8Prime> Tuple8<_1, _2, _3, _4, _5, _6, _7, _8Prime> flatMap( + Function>> f) { + return pure(f.apply(_8).>coerce()._8()); + } + + @Override + public <_8Prime, App extends Applicative> Applicative, App> traverse( + Function> fn, + Function>, ? extends Applicative>, App>> pure) { + return fn.apply(_8).fmap(_8Prime -> fmap(constantly(_8Prime))); + } + + /** + * Given a value of type A, produced an instance of this tuple with each slot set to that value. + * + * @param a the value to fill the tuple with + * @param the value type + * @return the filled tuple + * @see Tuple2#fill + */ + public static Tuple8 fill(A a) { + return tuple(a, a, a, a, a, a, a, a); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java new file mode 100644 index 000000000..b86a683a6 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java @@ -0,0 +1,102 @@ +package com.jnape.palatable.lambda.adt.hlist; + +import com.jnape.palatable.lambda.adt.hlist.HList.HCons; +import com.jnape.palatable.traitor.annotations.TestTraits; +import com.jnape.palatable.traitor.runners.Traits; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import testsupport.traits.ApplicativeLaws; +import testsupport.traits.BifunctorLaws; +import testsupport.traits.FunctorLaws; +import testsupport.traits.MonadLaws; +import testsupport.traits.TraversableLaws; + +import java.util.function.Function; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +@RunWith(Traits.class) +public class Tuple6Test { + + private Tuple6 tuple6; + + @Before + public void setUp() { + tuple6 = new Tuple6<>(2.0f, new Tuple5<>(1, new Tuple4<>("2", new Tuple3<>('3', new Tuple2<>(false, new SingletonHList<>(5L)))))); + } + + @TestTraits({FunctorLaws.class, ApplicativeLaws.class, MonadLaws.class, BifunctorLaws.class, TraversableLaws.class}) + public Tuple6 testSubject() { + return tuple("one", 2, 3d, 4f, '5', (byte) 6); + } + + @Test + public void head() { + assertEquals((Float) 2.0F, tuple6.head()); + } + + @Test + public void tail() { + assertEquals(new Tuple5<>(1, new Tuple4<>("2", new Tuple3<>('3', new Tuple2<>(false, new SingletonHList<>(5L))))), + tuple6.tail()); + } + + @Test + public void cons() { + assertEquals(new HCons<>(0, tuple6), tuple6.cons(0)); + } + + @Test + public void accessors() { + assertEquals((Float) 2.0f, tuple6._1()); + assertEquals((Integer) 1, tuple6._2()); + assertEquals("2", tuple6._3()); + assertEquals((Character) '3', tuple6._4()); + assertEquals(false, tuple6._5()); + assertEquals((Long) 5L, tuple6._6()); + } + + @Test + public void randomAccess() { + Tuple5 spiedTail = spy(tuple("second", "third", "fourth", "fifth", "sixth")); + Tuple6 tuple6 = new Tuple6<>("first", spiedTail); + + verify(spiedTail, times(1))._1(); + verify(spiedTail, times(1))._2(); + verify(spiedTail, times(1))._3(); + verify(spiedTail, times(1))._4(); + verify(spiedTail, times(1))._5(); + tuple6._1(); + tuple6._2(); + tuple6._3(); + tuple6._4(); + tuple6._5(); + tuple6._6(); + verifyNoMoreInteractions(spiedTail); + } + + @Test + public void fill() { + assertEquals(tuple("foo", "foo", "foo", "foo", "foo", "foo"), Tuple6.fill("foo")); + } + + @Test + public void zipPrecedence() { + Tuple6 a = tuple("foo", 1, 2, 3, 4, 5); + Tuple6> b = tuple("bar", 2, 3, 4, 5, x -> x + 1); + assertEquals(tuple("foo", 1, 2, 3, 4, 6), a.zip(b)); + } + + @Test + public void flatMapPrecedence() { + Tuple6 a = tuple("foo", 1, 2, 3, 4, 5); + Function> b = x -> tuple("bar", 2, 3, 4, 5, x + 1); + assertEquals(tuple("foo", 1, 2, 3, 4, 6), a.flatMap(b)); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java new file mode 100644 index 000000000..d9fee2832 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java @@ -0,0 +1,105 @@ +package com.jnape.palatable.lambda.adt.hlist; + +import com.jnape.palatable.lambda.adt.hlist.HList.HCons; +import com.jnape.palatable.traitor.annotations.TestTraits; +import com.jnape.palatable.traitor.runners.Traits; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import testsupport.traits.ApplicativeLaws; +import testsupport.traits.BifunctorLaws; +import testsupport.traits.FunctorLaws; +import testsupport.traits.MonadLaws; +import testsupport.traits.TraversableLaws; + +import java.util.function.Function; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +@RunWith(Traits.class) +public class Tuple7Test { + + private Tuple7 tuple7; + + @Before + public void setUp() { + tuple7 = new Tuple7<>((byte) 127, new Tuple6<>(2.0f, new Tuple5<>(1, new Tuple4<>("2", new Tuple3<>('3', new Tuple2<>(false, new SingletonHList<>(5L))))))); + } + + @TestTraits({FunctorLaws.class, ApplicativeLaws.class, MonadLaws.class, BifunctorLaws.class, TraversableLaws.class}) + public Tuple7 testSubject() { + return tuple("one", 2, 3d, 4f, '5', (byte) 6, 7L); + } + + @Test + public void head() { + assertEquals((Byte) (byte) 127, tuple7.head()); + } + + @Test + public void tail() { + assertEquals(new Tuple6<>(2.0f, new Tuple5<>(1, new Tuple4<>("2", new Tuple3<>('3', new Tuple2<>(false, new SingletonHList<>(5L)))))), + tuple7.tail()); + } + + @Test + public void cons() { + assertEquals(new HCons<>(0, tuple7), tuple7.cons(0)); + } + + @Test + public void accessors() { + assertEquals((Byte) (byte) 127, tuple7._1()); + assertEquals((Float) 2.0f, tuple7._2()); + assertEquals((Integer) 1, tuple7._3()); + assertEquals("2", tuple7._4()); + assertEquals((Character) '3', tuple7._5()); + assertEquals(false, tuple7._6()); + assertEquals((Long) 5L, tuple7._7()); + } + + @Test + public void randomAccess() { + Tuple6 spiedTail = spy(tuple("second", "third", "fourth", "fifth", "sixth", "seventh")); + Tuple7 tuple7 = new Tuple7<>("first", spiedTail); + + verify(spiedTail, times(1))._1(); + verify(spiedTail, times(1))._2(); + verify(spiedTail, times(1))._3(); + verify(spiedTail, times(1))._4(); + verify(spiedTail, times(1))._5(); + verify(spiedTail, times(1))._6(); + tuple7._1(); + tuple7._2(); + tuple7._3(); + tuple7._4(); + tuple7._5(); + tuple7._6(); + tuple7._7(); + verifyNoMoreInteractions(spiedTail); + } + + @Test + public void fill() { + assertEquals(tuple("foo", "foo", "foo", "foo", "foo", "foo", "foo"), Tuple7.fill("foo")); + } + + @Test + public void zipPrecedence() { + Tuple7 a = tuple("foo", 1, 2, 3, 4, 5, 6); + Tuple7> b = tuple("bar", 2, 3, 4, 5, 6, x -> x + 1); + assertEquals(tuple("foo", 1, 2, 3, 4, 5, 7), a.zip(b)); + } + + @Test + public void flatMapPrecedence() { + Tuple7 a = tuple("foo", 1, 2, 3, 4, 5, 6); + Function> b = x -> tuple("bar", 2, 3, 4, 5, 6, x + 1); + assertEquals(tuple("foo", 1, 2, 3, 4, 5, 7), a.flatMap(b)); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java new file mode 100644 index 000000000..7044715b6 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java @@ -0,0 +1,108 @@ +package com.jnape.palatable.lambda.adt.hlist; + +import com.jnape.palatable.lambda.adt.hlist.HList.HCons; +import com.jnape.palatable.traitor.annotations.TestTraits; +import com.jnape.palatable.traitor.runners.Traits; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import testsupport.traits.ApplicativeLaws; +import testsupport.traits.BifunctorLaws; +import testsupport.traits.FunctorLaws; +import testsupport.traits.MonadLaws; +import testsupport.traits.TraversableLaws; + +import java.util.function.Function; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +@RunWith(Traits.class) +public class Tuple8Test { + + private Tuple8 tuple8; + + @Before + public void setUp() { + tuple8 = new Tuple8<>((short) 65535, new Tuple7<>((byte) 127, new Tuple6<>(2.0f, new Tuple5<>(1, new Tuple4<>("2", new Tuple3<>('3', new Tuple2<>(false, new SingletonHList<>(5L)))))))); + } + + @TestTraits({FunctorLaws.class, ApplicativeLaws.class, MonadLaws.class, BifunctorLaws.class, TraversableLaws.class}) + public Tuple8 testSubject() { + return tuple("one", 2, 3d, 4f, '5', (byte) 6, 7L, (short) 65535); + } + + @Test + public void head() { + assertEquals((Short) (short) 65535, tuple8.head()); + } + + @Test + public void tail() { + assertEquals(new Tuple7<>((byte) 127, new Tuple6<>(2.0f, new Tuple5<>(1, new Tuple4<>("2", new Tuple3<>('3', new Tuple2<>(false, new SingletonHList<>(5L))))))), + tuple8.tail()); + } + + @Test + public void cons() { + assertEquals(new HCons<>(0, tuple8), tuple8.cons(0)); + } + + @Test + public void accessors() { + assertEquals((Short) (short) 65535, tuple8._1()); + assertEquals((Byte) (byte) 127, tuple8._2()); + assertEquals((Float) 2.0f, tuple8._3()); + assertEquals((Integer) 1, tuple8._4()); + assertEquals("2", tuple8._5()); + assertEquals((Character) '3', tuple8._6()); + assertEquals(false, tuple8._7()); + assertEquals((Long) 5L, tuple8._8()); + } + + @Test + public void randomAccess() { + Tuple7 spiedTail = spy(tuple("second", "third", "fourth", "fifth", "sixth", "seventh", "eighth")); + Tuple8 tuple8 = new Tuple8<>("first", spiedTail); + + verify(spiedTail, times(1))._1(); + verify(spiedTail, times(1))._2(); + verify(spiedTail, times(1))._3(); + verify(spiedTail, times(1))._4(); + verify(spiedTail, times(1))._5(); + verify(spiedTail, times(1))._6(); + verify(spiedTail, times(1))._7(); + tuple8._1(); + tuple8._2(); + tuple8._3(); + tuple8._4(); + tuple8._5(); + tuple8._6(); + tuple8._7(); + tuple8._8(); + verifyNoMoreInteractions(spiedTail); + } + + @Test + public void fill() { + assertEquals(tuple("foo", "foo", "foo", "foo", "foo", "foo", "foo", "foo"), Tuple8.fill("foo")); + } + + @Test + public void zipPrecedence() { + Tuple8 a = tuple("foo", 1, 2, 3, 4, 5, 6, 7); + Tuple8> b = tuple("bar", 2, 3, 4, 5, 6, 7, x -> x + 1); + assertEquals(tuple("foo", 1, 2, 3, 4, 5, 6, 8), a.zip(b)); + } + + @Test + public void flatMapPrecedence() { + Tuple8 a = tuple("foo", 1, 2, 3, 4, 5, 6, 7); + Function> b = x -> tuple("bar", 2, 3, 4, 5, 6, 7, x + 1); + assertEquals(tuple("foo", 1, 2, 3, 4, 5, 6, 8), a.flatMap(b)); + } +} \ No newline at end of file From 1a09821a7c503eed02bcae700a01454476b37bd1 Mon Sep 17 00:00:00 2001 From: jnape Date: Sat, 18 Nov 2017 17:00:54 -0600 Subject: [PATCH 04/24] Adding Fn3/Fn4 static factory methods --- CHANGELOG.md | 1 + .../jnape/palatable/lambda/functions/Fn3.java | 30 ++++++++++++- .../jnape/palatable/lambda/functions/Fn4.java | 45 +++++++++++++++++++ .../palatable/lambda/functions/Fn3Test.java | 10 +++++ .../palatable/lambda/functions/Fn4Test.java | 44 ++++++++++++++++++ 5 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/Fn4Test.java diff --git a/CHANGELOG.md b/CHANGELOG.md index c67077849..dfdb7deee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] ### Added - `Tuple6` through `Tuple8` +- `Fn3#fn3` and `Fn4#fn4` static factory methods ## [2.0.0] - 2017-11-13 ### Changed diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn3.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn3.java index 7e21f9d79..f9cd66e2e 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/Fn3.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn3.java @@ -61,10 +61,38 @@ default Fn3 flip() { * Returns an Fn2 that takes the first two arguments as a Tuple2<A, B> and the third * argument. * - * @return an Fn2 taking a Tuple2 and the third argument + * @return an Fn2 taking a Tuple2 `and the third argument */ @Override default Fn2, C, D> uncurry() { return (ab, c) -> apply(ab._1(), ab._2(), c); } + + /** + * Static factory method for wrapping a curried {@link Fn1} in an {@link Fn3}. + * + * @param curriedFn1 the curried fn1 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the output type + * @return the Fn3 + */ + static Fn3 fn3(Fn1> curriedFn1) { + return (a, b, c) -> curriedFn1.apply(a).apply(b, c); + } + + /** + * Static factory method for wrapping a curried {@link Fn2} in an {@link Fn3}. + * + * @param curriedFn2 the curried fn2 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the output type + * @return the Fn3 + */ + static Fn3 fn3(Fn2> curriedFn2) { + return (a, b, c) -> curriedFn2.apply(a, b).apply(c); + } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn4.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn4.java index cb093a7f1..98e675ba1 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/Fn4.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn4.java @@ -82,4 +82,49 @@ default Fn4 flip() { default Fn3, C, D, E> uncurry() { return (ab, c, d) -> apply(ab._1(), ab._2(), c, d); } + + /** + * Static factory method for wrapping a curried {@link Fn1} in an {@link Fn4}. + * + * @param curriedFn1 the curried fn1 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the output type + * @return the Fn4 + */ + static Fn4 fn4(Fn1> curriedFn1) { + return (a, b, c, d) -> curriedFn1.apply(a).apply(b, c, d); + } + + /** + * Static factory method for wrapping a curried {@link Fn2} in an {@link Fn4}. + * + * @param curriedFn2 the curried fn2 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the output type + * @return the Fn4 + */ + static Fn4 fn4(Fn2> curriedFn2) { + return (a, b, c, d) -> curriedFn2.apply(a, b).apply(c, d); + } + + /** + * Static factory method for wrapping a curried {@link Fn3} in an {@link Fn4}. + * + * @param curriedFn3 the curried fn3 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the output type + * @return the Fn4 + */ + static Fn4 fn4(Fn3> curriedFn3) { + return (a, b, c, d) -> curriedFn3.apply(a, b, c).apply(d); + } } diff --git a/src/test/java/com/jnape/palatable/lambda/functions/Fn3Test.java b/src/test/java/com/jnape/palatable/lambda/functions/Fn3Test.java index 7214064f2..31f00e249 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/Fn3Test.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/Fn3Test.java @@ -4,6 +4,7 @@ import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; public class Fn3Test { @@ -28,4 +29,13 @@ public void flipsFirstAndSecondArgument() { public void uncurries() { assertThat(CHECK_MULTIPLICATION.uncurry().apply(tuple(2, 3), 6), is(true)); } + + @Test + public void fn3() { + Fn1> fn1 = a -> (b, c) -> a + b + c; + assertEquals("abc", Fn3.fn3(fn1).apply("a", "b", "c")); + + Fn2> fn2 = (a, b) -> c -> a + b + c; + assertEquals("abc", Fn3.fn3(fn2).apply("a", "b", "c")); + } } diff --git a/src/test/java/com/jnape/palatable/lambda/functions/Fn4Test.java b/src/test/java/com/jnape/palatable/lambda/functions/Fn4Test.java new file mode 100644 index 000000000..f241fc88a --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/Fn4Test.java @@ -0,0 +1,44 @@ +package com.jnape.palatable.lambda.functions; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +public class Fn4Test { + + private static final Fn4 APPEND = + (s1, s2, s3, s4) -> s1 + s2 + s3 + s4; + + @Test + public void canBePartiallyApplied() { + assertThat(APPEND.apply("a").apply("b").apply("c").apply("d"), is("abcd")); + assertThat(APPEND.apply("a").apply("b").apply("c", "d"), is("abcd")); + assertThat(APPEND.apply("a").apply("b", "c", "d"), is("abcd")); + assertThat(APPEND.apply("a", "b", "c", "d"), is("abcd")); + } + + @Test + public void flipsFirstAndSecondArgument() { + assertThat(APPEND.flip().apply("a", "b", "c", "d"), is("bacd")); + } + + @Test + public void uncurries() { + assertThat(APPEND.uncurry().apply(tuple("a", "b"), "c", "d"), is("abcd")); + } + + @Test + public void fn4() { + Fn1> fn1 = a -> (b, c, d) -> a + b + c + d; + assertEquals("abcd", Fn4.fn4(fn1).apply("a", "b", "c", "d")); + + Fn2> fn2 = (a, b) -> (c, d) -> a + b + c + d; + assertEquals("abcd", Fn4.fn4(fn2).apply("a", "b", "c", "d")); + + Fn3> fn3 = (a, b, c) -> (d) -> a + b + c + d; + assertEquals("abcd", Fn4.fn4(fn3).apply("a", "b", "c", "d")); + } +} From 5b3121f3b7492a809a8853e71aefbbf7d4f39739 Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 19 Nov 2017 01:36:24 -0600 Subject: [PATCH 05/24] Adding Fn5-8; adding #into on Tuple5-8 --- CHANGELOG.md | 4 +- README.md | 2 +- .../palatable/lambda/adt/hlist/Tuple5.java | 14 + .../palatable/lambda/adt/hlist/Tuple6.java | 13 + .../palatable/lambda/adt/hlist/Tuple7.java | 15 + .../palatable/lambda/adt/hlist/Tuple8.java | 15 + .../jnape/palatable/lambda/functions/Fn1.java | 22 +- .../jnape/palatable/lambda/functions/Fn2.java | 55 ++- .../jnape/palatable/lambda/functions/Fn3.java | 49 ++- .../jnape/palatable/lambda/functions/Fn4.java | 53 ++- .../jnape/palatable/lambda/functions/Fn5.java | 196 +++++++++++ .../jnape/palatable/lambda/functions/Fn6.java | 234 +++++++++++++ .../jnape/palatable/lambda/functions/Fn7.java | 275 +++++++++++++++ .../jnape/palatable/lambda/functions/Fn8.java | 325 ++++++++++++++++++ .../lambda/adt/hlist/Tuple5Test.java | 6 + .../lambda/adt/hlist/Tuple6Test.java | 6 + .../lambda/adt/hlist/Tuple7Test.java | 6 + .../lambda/adt/hlist/Tuple8Test.java | 6 + .../palatable/lambda/functions/Fn5Test.java | 48 +++ .../palatable/lambda/functions/Fn6Test.java | 52 +++ .../palatable/lambda/functions/Fn7Test.java | 56 +++ .../palatable/lambda/functions/Fn8Test.java | 60 ++++ 22 files changed, 1466 insertions(+), 46 deletions(-) create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/Fn5.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/Fn6.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/Fn7.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/Fn8.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/Fn5Test.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/Fn6Test.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/Fn7Test.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/Fn8Test.java diff --git a/CHANGELOG.md b/CHANGELOG.md index dfdb7deee..f95faaa37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] ### Added -- `Tuple6` through `Tuple8` - `Fn3#fn3` and `Fn4#fn4` static factory methods +- `Fn5` through `Fn8` +- `Tuple5#into` +- `Tuple6` through `Tuple8` ## [2.0.0] - 2017-11-13 ### Changed diff --git a/README.md b/README.md index a4e955801..97c7ff6a0 100644 --- a/README.md +++ b/README.md @@ -338,7 +338,7 @@ In addition to implementing `fmap` from `Functor`, implementing an applicative f ### Monads -Monads are applicative functors that additionally support a chaining operation, `flatMap :: (a -> f b) -> f a -> f b`: a function from the functor's parameter to a new instance of the same functor over a potentially different parameter. Because the function passed to `flatMap` can return a different instance of the same functor, functors can take advantage of multiple constructions that yield different results functorial operations, like short-circuiting, as in the following example using `Either`: +Monads are applicative functors that additionally support a chaining operation, `flatMap :: (a -> f b) -> f a -> f b`: a function from the functor's parameter to a new instance of the same functor over a potentially different parameter. Because the function passed to `flatMap` can return a different instance of the same functor, functors can take advantage of multiple constructions that yield different functorial operations, like short-circuiting, as in the following example using `Either`: ```Java class Person { diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java index ac205abbc..065aef622 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple5.java @@ -1,6 +1,7 @@ package com.jnape.palatable.lambda.adt.hlist; import com.jnape.palatable.lambda.adt.hlist.HList.HCons; +import com.jnape.palatable.lambda.functions.Fn5; import com.jnape.palatable.lambda.functor.Applicative; import com.jnape.palatable.lambda.functor.Bifunctor; import com.jnape.palatable.lambda.monad.Monad; @@ -91,6 +92,19 @@ public _5 _5() { return _5; } + /** + * Destructure and apply this tuple to a function accepting the same number of arguments as this tuple's + * slots. + * + * @param fn the function to apply + * @param the return type of the function + * @return the result of applying the destructured tuple to the function + * @see Tuple2#into + */ + public R into(Fn5 fn) { + return fn.apply(_1, _2, _3, _4, _5); + } + @Override public <_5Prime> Tuple5<_1, _2, _3, _4, _5Prime> fmap(Function fn) { return Monad.super.<_5Prime>fmap(fn).coerce(); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java index 50cfcd016..5bba3c052 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple6.java @@ -1,6 +1,7 @@ package com.jnape.palatable.lambda.adt.hlist; import com.jnape.palatable.lambda.adt.hlist.HList.HCons; +import com.jnape.palatable.lambda.functions.Fn6; import com.jnape.palatable.lambda.functor.Applicative; import com.jnape.palatable.lambda.functor.Bifunctor; import com.jnape.palatable.lambda.monad.Monad; @@ -104,6 +105,18 @@ public _6 _6() { return _6; } + /** + * Destructure and apply this tuple to a function accepting the same number of arguments as this tuple's + * slots. + * + * @param fn the function to apply + * @param the return type of the function + * @return the result of applying the destructured tuple to the function + * @see Tuple2#into + */ + public R into(Fn6 fn) { + return fn.apply(_1, _2, _3, _4, _5, _6); + } @Override public <_6Prime> Tuple6<_1, _2, _3, _4, _5, _6Prime> fmap(Function fn) { diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java index 6b74d30eb..bbe9ec79d 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple7.java @@ -1,6 +1,7 @@ package com.jnape.palatable.lambda.adt.hlist; import com.jnape.palatable.lambda.adt.hlist.HList.HCons; +import com.jnape.palatable.lambda.functions.Fn7; import com.jnape.palatable.lambda.functor.Applicative; import com.jnape.palatable.lambda.functor.Bifunctor; import com.jnape.palatable.lambda.monad.Monad; @@ -117,6 +118,20 @@ public _7 _7() { return _7; } + /** + * Destructure and apply this tuple to a function accepting the same number of arguments as this tuple's + * slots. + * + * @param fn the function to apply + * @param the return type of the function + * @return the result of applying the destructured tuple to the function + * @see Tuple2#into + */ + public R into( + Fn7 fn) { + return fn.apply(_1, _2, _3, _4, _5, _6, _7); + } + @Override public <_7Prime> Tuple7<_1, _2, _3, _4, _5, _6, _7Prime> fmap(Function fn) { return Monad.super.<_7Prime>fmap(fn).coerce(); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java index 8f1b24bef..2a1d1615d 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple8.java @@ -1,6 +1,7 @@ package com.jnape.palatable.lambda.adt.hlist; import com.jnape.palatable.lambda.adt.hlist.HList.HCons; +import com.jnape.palatable.lambda.functions.Fn8; import com.jnape.palatable.lambda.functor.Applicative; import com.jnape.palatable.lambda.functor.Bifunctor; import com.jnape.palatable.lambda.monad.Monad; @@ -130,6 +131,20 @@ public _8 _8() { return _8; } + /** + * Destructure and apply this tuple to a function accepting the same number of arguments as this tuple's + * slots. + * + * @param fn the function to apply + * @param the return type of the function + * @return the result of applying the destructured tuple to the function + * @see Tuple2#into + */ + public R into( + Fn8 fn) { + return fn.apply(_1, _2, _3, _4, _5, _6, _7, _8); + } + @Override public <_8Prime> Tuple8<_1, _2, _3, _4, _5, _6, _7, _8Prime> fmap(Function fn) { return Monad.super.<_8Prime>fmap(fn).coerce(); diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn1.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn1.java index b53d317c6..608d57fd5 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/Fn1.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn1.java @@ -90,7 +90,7 @@ default Fn1 discardR(Applicative> appB) { * * @param the new argument type * @param fn the contravariant argument mapping function - * @return a new function from Z (the new argument type) to B (the same result) + * @return an {@link Fn1}<Z, B> */ @Override default Fn1 diMapL(Function fn) { @@ -103,7 +103,7 @@ default Fn1 diMapL(Function fn) { * * @param the new result type * @param fn the covariant result mapping function - * @return a new function from A (the same argument type) to C (the new result type) + * @return an {@link Fn1}<A, C> */ @Override default Fn1 diMapR(Function fn) { @@ -117,7 +117,7 @@ default Fn1 diMapR(Function fn) { * @param the new result type * @param lFn the contravariant argument mapping function * @param rFn the covariant result mapping function - * @return a new function from Z (the new argument type) to C (the new result type) + * @return an {@link Fn1}<Z, C> */ @Override default Fn1 diMap(Function lFn, Function rFn) { @@ -130,12 +130,12 @@ default Fn1 contraMap(Function fn) { } /** - * Override of {@link Function#compose(Function)}, returning an instance of Fn1 for compatibility. + * Override of {@link Function#compose(Function)}, returning an instance of {@link Fn1} for compatibility. * Right-to-left composition. * * @param before the function who's return value is this function's argument * @param the new argument type - * @return a new function from Z (the new argument type) to B (the same result type) + * @return an {@link Fn1}<Z, B> */ @Override default Fn1 compose(Function before) { @@ -149,7 +149,7 @@ default Fn1 compose(Function before) { * @param before the function to pass its return value to this function's input * @param the resulting function's first argument type * @param the resulting function's second argument type - * @return a new function from Y x Z to B + * @return an {@link Fn2}<Y, Z, B> */ default Fn2 compose(BiFunction before) { return compose(fn2(before)); @@ -161,7 +161,7 @@ default Fn2 compose(BiFunction the resulting function's first argument type * @param the resulting function's second argument type - * @return a new function from Y x Z to B + * @return an {@link Fn2}<Y, Z, B> */ default Fn2 compose(Fn2 before) { return fn2(before.fmap(this::compose))::apply; @@ -174,19 +174,19 @@ default Fn2 compose(Fn2 befor * @param after the function to invoke on this function's return value * @param the resulting function's second argument type * @param the resulting function's return type - * @return a new function from A x C to D + * @return an {@link Fn2}<A, C, D> */ default Fn2 andThen(BiFunction after) { return (a, c) -> after.apply(apply(a), c); } /** - * Override of {@link Function#andThen(Function)}, returning an instance of Fn1 for compatibility. + * Override of {@link Function#andThen(Function)}, returning an instance of {@link Fn1} for compatibility. * Left-to-right composition. * * @param after the function to invoke on this function's return value * @param the new result type - * @return a new function from A (the same argument type) to C (the new result type) + * @return an {@link Fn1}<A, C> */ @Override default Fn1 andThen(Function after) { @@ -200,7 +200,7 @@ default Fn1 andThen(Function after) { * @param function the function to adapt * @param the input argument type * @param the output type - * @return the Fn1 + * @return the {@link Fn1} */ static Fn1 fn1(Function function) { return function::apply; diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn2.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn2.java index 792309b09..712676d66 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/Fn2.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn2.java @@ -1,14 +1,18 @@ package com.jnape.palatable.lambda.functions; import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import com.jnape.palatable.lambda.functor.Applicative; import java.util.function.BiFunction; import java.util.function.Function; +import static com.jnape.palatable.lambda.functions.Fn3.fn3; + /** - * A function taking two arguments. Note that defining Fn2 in terms of Fn1 provides a - * reasonable approximation of currying in the form of multiple apply overloads that take different numbers - * of arguments. + * A function taking two arguments. + *

+ * Note that defining {@link Fn2} in terms of Fn1 provides a reasonable approximation of currying in the + * form of multiple {@link Fn2#apply} overloads that take different numbers of arguments. * * @param The first argument type * @param The second argument type @@ -28,11 +32,11 @@ public interface Fn2 extends Fn1> { C apply(A a, B b); /** - * Same as normal composition, except that the result is an instance of Fn2 for convenience. + * Same as normal composition, except that the result is an instance of {@link Fn2} for convenience. * * @param before the function who's return value is this function's argument * @param the new argument type - * @return a new Fn2<Z,B,C> + * @return an {@link Fn2}<Z, B, C> */ @Override default Fn2 compose(Function before) { @@ -43,7 +47,7 @@ default Fn2 compose(Function before) { * Partially apply this function by passing its first argument. * * @param a the first argument - * @return an Fn1 that takes the second argument and returns the result + * @return an {@link Fn1}<B, C> */ @Override default Fn1 apply(A a) { @@ -53,31 +57,56 @@ default Fn1 apply(A a) { /** * Flip the order of the arguments. * - * @return an Fn2 that takes the first and second arguments in reversed order + * @return an {@link Fn2}<B, A, C> */ default Fn2 flip() { return (b, a) -> apply(a, b); } /** - * Returns an Fn1 that takes the arguments as a Tuple2<A, B>. + * Returns an {@link Fn1} that takes the arguments as a {@link Tuple2}<A, B>. * - * @return an Fn1 taking a Tuple2 + * @return an {@link Fn1} taking a {@link Tuple2} */ default Fn1, C> uncurry() { return (ab) -> apply(ab._1(), ab._2()); } /** - * View this Fn2 as a j.u.f.BiFunction. + * View this {@link Fn2} as a {@link BiFunction}. * - * @return the same logic as a BiFunction + * @return the same logic as a {@link BiFunction} * @see BiFunction */ default BiFunction toBiFunction() { return this::apply; } + @Override + default Fn2 discardR(Applicative> appB) { + return fn2(Fn1.super.discardR(appB)); + } + + @Override + default Fn2 diMapL(Function fn) { + return fn2(Fn1.super.diMapL(fn)); + } + + @Override + default Fn2 contraMap(Function fn) { + return fn2(Fn1.super.contraMap(fn)); + } + + @Override + default Fn3 compose(BiFunction before) { + return fn3(Fn1.super.compose(before)); + } + + @Override + default Fn3 compose(Fn2 before) { + return fn3(Fn1.super.compose(before)); + } + /** * Static factory method for wrapping a {@link BiFunction} in an {@link Fn2}. Useful for avoid explicit casting when * using method references as {@link Fn2}s. @@ -86,7 +115,7 @@ default BiFunction toBiFunction() { * @param the first input argument type * @param the second input argument type * @param the output type - * @return the Fn2 + * @return the {@link Fn2} */ static Fn2 fn2(BiFunction biFunction) { return biFunction::apply; @@ -99,7 +128,7 @@ static Fn2 fn2(BiFunction * @param the first input argument type * @param the second input argument type * @param the output type - * @return the Fn2 + * @return the {@link Fn2} */ static Fn2 fn2(Fn1> curriedFn1) { return (a, b) -> curriedFn1.apply(a).apply(b); diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn3.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn3.java index f9cd66e2e..34d725aed 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/Fn3.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn3.java @@ -1,9 +1,15 @@ package com.jnape.palatable.lambda.functions; import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import com.jnape.palatable.lambda.functor.Applicative; + +import java.util.function.BiFunction; +import java.util.function.Function; + +import static com.jnape.palatable.lambda.functions.Fn4.fn4; /** - * A function taking three arguments. Defined in terms of Fn2, so similarly auto-curried. + * A function taking three arguments. Defined in terms of {@link Fn2}, so similarly auto-curried. * * @param The first argument type * @param The second argument type @@ -28,7 +34,7 @@ public interface Fn3 extends Fn2> { * Partially apply this function by taking its first argument. * * @param a the first argument - * @return an Fn2 that takes the second and third argument and returns the result + * @return an {@link Fn2}<B, C, D> */ @Override default Fn2 apply(A a) { @@ -40,7 +46,7 @@ default Fn2 apply(A a) { * * @param a the first argument * @param b the second argument - * @return an Fn1 that takes the third argument and returns the result + * @return an {@link Fn1}<C, D> */ @Override default Fn1 apply(A a, B b) { @@ -50,7 +56,7 @@ default Fn1 apply(A a, B b) { /** * Flip the order of the first two arguments. * - * @return an Fn3 that takes the first and second arguments in reversed order + * @return an {@link Fn3}<B, A, C, D> */ @Override default Fn3 flip() { @@ -58,16 +64,41 @@ default Fn3 flip() { } /** - * Returns an Fn2 that takes the first two arguments as a Tuple2<A, B> and the third - * argument. + * Returns an {@link Fn2} that takes the first two arguments as a {@link Tuple2}<A, B> and the + * third argument. * - * @return an Fn2 taking a Tuple2 `and the third argument + * @return an {@link Fn2} taking a {@link Tuple2} and the third argument */ @Override default Fn2, C, D> uncurry() { return (ab, c) -> apply(ab._1(), ab._2(), c); } + @Override + default Fn3 discardR(Applicative> appB) { + return fn3(Fn2.super.discardR(appB)); + } + + @Override + default Fn3 diMapL(Function fn) { + return fn3(Fn2.super.diMapL(fn)); + } + + @Override + default Fn3 contraMap(Function fn) { + return fn3(Fn2.super.contraMap(fn)); + } + + @Override + default Fn4 compose(BiFunction before) { + return fn4(Fn2.super.compose(before)); + } + + @Override + default Fn4 compose(Fn2 before) { + return fn4(Fn2.super.compose(before)); + } + /** * Static factory method for wrapping a curried {@link Fn1} in an {@link Fn3}. * @@ -76,7 +107,7 @@ default Fn2, C, D> uncurry() { * @param the second input argument type * @param the third input argument type * @param the output type - * @return the Fn3 + * @return the {@link Fn3} */ static Fn3 fn3(Fn1> curriedFn1) { return (a, b, c) -> curriedFn1.apply(a).apply(b, c); @@ -90,7 +121,7 @@ static Fn3 fn3(Fn1> curriedFn1) { * @param the second input argument type * @param the third input argument type * @param the output type - * @return the Fn3 + * @return the {@link Fn3} */ static Fn3 fn3(Fn2> curriedFn2) { return (a, b, c) -> curriedFn2.apply(a, b).apply(c); diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn4.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn4.java index 98e675ba1..e98bb9da9 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/Fn4.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn4.java @@ -1,9 +1,15 @@ package com.jnape.palatable.lambda.functions; import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import com.jnape.palatable.lambda.functor.Applicative; + +import java.util.function.BiFunction; +import java.util.function.Function; + +import static com.jnape.palatable.lambda.functions.Fn5.fn5; /** - * A function taking four arguments. Defined in terms of Fn3, so similarly auto-curried. + * A function taking four arguments. Defined in terms of {@link Fn3}, so similarly auto-curried. * * @param The first argument type * @param The second argument type @@ -30,7 +36,7 @@ public interface Fn4 extends Fn3> { * Partially apply this function by taking its first argument. * * @param a the first argument - * @return an Fn3 that takes the second, third, and fourth argument and returns the result + * @return an {@link Fn3}<B, C, D, E> */ @Override default Fn3 apply(A a) { @@ -42,7 +48,7 @@ default Fn3 apply(A a) { * * @param a the first argument * @param b the second argument - * @return an Fn2 that takes the third and fourth arguments and returns the result + * @return an {@link Fn2}<C, D, E> */ @Override default Fn2 apply(A a, B b) { @@ -55,7 +61,7 @@ default Fn2 apply(A a, B b) { * @param a the first argument * @param b the second argument * @param c the third argument - * @return an Fn1 that takes the fourth argument and returns the result + * @return an {@link Fn1}<D, E> */ @Override default Fn1 apply(A a, B b, C c) { @@ -65,7 +71,7 @@ default Fn1 apply(A a, B b, C c) { /** * Flip the order of the first two arguments. * - * @return an Fn3 that takes the first and second arguments in reversed order + * @return an {@link Fn4}<B, A, C, D, E> */ @Override default Fn4 flip() { @@ -73,16 +79,41 @@ default Fn4 flip() { } /** - * Returns an Fn3 that takes the first two arguments as a Tuple2<A, B> and the third - * and fourth arguments. + * Returns an {@link Fn3} that takes the first two arguments as a {@link Tuple2}<A, B> and the + * third and fourth arguments. * - * @return an Fn3 taking a Tuple2 and the third and fourth arguments + * @return an {@link Fn3} taking a {@link Tuple2} and the third and fourth arguments */ @Override default Fn3, C, D, E> uncurry() { return (ab, c, d) -> apply(ab._1(), ab._2(), c, d); } + @Override + default Fn4 discardR(Applicative> appB) { + return fn4(Fn3.super.discardR(appB)); + } + + @Override + default Fn4 diMapL(Function fn) { + return fn4(Fn3.super.diMapL(fn)); + } + + @Override + default Fn4 contraMap(Function fn) { + return fn4(Fn3.super.contraMap(fn)); + } + + @Override + default Fn5 compose(BiFunction before) { + return fn5(Fn3.super.compose(before)); + } + + @Override + default Fn5 compose(Fn2 before) { + return fn5(Fn3.super.compose(before)); + } + /** * Static factory method for wrapping a curried {@link Fn1} in an {@link Fn4}. * @@ -92,7 +123,7 @@ default Fn3, C, D, E> uncurry() { * @param the third input argument type * @param the fourth input argument type * @param the output type - * @return the Fn4 + * @return the {@link Fn4} */ static Fn4 fn4(Fn1> curriedFn1) { return (a, b, c, d) -> curriedFn1.apply(a).apply(b, c, d); @@ -107,7 +138,7 @@ static Fn4 fn4(Fn1> curriedFn1 * @param the third input argument type * @param the fourth input argument type * @param the output type - * @return the Fn4 + * @return the {@link Fn4} */ static Fn4 fn4(Fn2> curriedFn2) { return (a, b, c, d) -> curriedFn2.apply(a, b).apply(c, d); @@ -122,7 +153,7 @@ static Fn4 fn4(Fn2> curriedFn2 * @param the third input argument type * @param the fourth input argument type * @param the output type - * @return the Fn4 + * @return the {@link Fn4} */ static Fn4 fn4(Fn3> curriedFn3) { return (a, b, c, d) -> curriedFn3.apply(a, b, c).apply(d); diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn5.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn5.java new file mode 100644 index 000000000..7e8b5abdf --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn5.java @@ -0,0 +1,196 @@ +package com.jnape.palatable.lambda.functions; + +import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import com.jnape.palatable.lambda.functor.Applicative; + +import java.util.function.BiFunction; +import java.util.function.Function; + +import static com.jnape.palatable.lambda.functions.Fn6.fn6; + +/** + * A function taking five arguments. Defined in terms of {@link Fn4}, so similarly auto-curried. + * + * @param The first argument type + * @param The second argument type + * @param The third argument type + * @param The fourth argument type + * @param The fifth argument type + * @param The return type + * @see Fn4 + */ +@FunctionalInterface +public interface Fn5 extends Fn4> { + + /** + * Invoke this function with the given arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @param d the fourth argument + * @param e the fifth argument + * @return the result of the function application + */ + F apply(A a, B b, C c, D d, E e); + + /** + * Partially apply this function by taking its first argument. + * + * @param a the first argument + * @return an {@link Fn5} that takes the remaining arguments and returns the result + */ + @Override + default Fn4 apply(A a) { + return (b, c, d, e) -> apply(a, b, c, d, e); + } + + /** + * Partially apply this function by taking its first two arguments. + * + * @param a the first argument + * @param b the second argument + * @return an {@link Fn3} that takes the remaining arguments and returns the result + */ + @Override + default Fn3 apply(A a, B b) { + return (c, d, e) -> apply(a, b, c, d, e); + } + + /** + * Partially apply this function by taking its first three arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @return an {@link Fn2} that takes remaining arguments and returns the result + */ + @Override + default Fn2 apply(A a, B b, C c) { + return (d, e) -> apply(a, b, c, d, e); + } + + /** + * Partially apply this function by taking its first four arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @param d the fourth argument + * @return an {@link Fn1} that takes the remaining argument and returns the result + */ + @Override + default Fn1 apply(A a, B b, C c, D d) { + return (e) -> apply(a, b, c, d, e); + } + + /** + * Flip the order of the first two arguments. + * + * @return an {@link Fn5} that takes the first and second arguments in reversed order + */ + @Override + default Fn5 flip() { + return (b, a, c, d, e) -> apply(a, b, c, d, e); + } + + /** + * Returns an {@link Fn4} that takes the first two arguments as a {@link Tuple2}<A, B> and the + * remaining arguments. + * + * @return an {@link Fn4} taking a {@link Tuple2} and the remaining arguments + */ + @Override + default Fn4, C, D, E, F> uncurry() { + return (ab, c, d, e) -> apply(ab._1(), ab._2(), c, d, e); + } + + @Override + default Fn5 discardR(Applicative> appB) { + return fn5(Fn4.super.discardR(appB)); + } + + @Override + default Fn5 diMapL(Function fn) { + return fn5(Fn4.super.diMapL(fn)); + } + + @Override + default Fn5 contraMap(Function fn) { + return fn5(Fn4.super.contraMap(fn)); + } + + @Override + default Fn6 compose(BiFunction before) { + return fn6(Fn4.super.compose(before)); + } + + @Override + default Fn6 compose(Fn2 before) { + return fn6(Fn4.super.compose(before)); + } + + /** + * Static factory method for wrapping a curried {@link Fn1} in an {@link Fn5}. + * + * @param curriedFn1 the curried fn1 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the output type + * @return the {@link Fn5} + */ + static Fn5 fn5(Fn1> curriedFn1) { + return (a, b, c, d, e) -> curriedFn1.apply(a).apply(b, c, d, e); + } + + /** + * Static factory method for wrapping a curried {@link Fn2} in an {@link Fn5}. + * + * @param curriedFn2 the curried fn2 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the output type + * @return the {@link Fn5} + */ + static Fn5 fn5(Fn2> curriedFn2) { + return (a, b, c, d, e) -> curriedFn2.apply(a, b).apply(c, d, e); + } + + /** + * Static factory method for wrapping a curried {@link Fn3} in an {@link Fn5}. + * + * @param curriedFn3 the curried fn3 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the output type + * @return the {@link Fn5} + */ + static Fn5 fn5(Fn3> curriedFn3) { + return (a, b, c, d, e) -> curriedFn3.apply(a, b, c).apply(d, e); + } + + /** + * Static factory method for wrapping a curried {@link Fn4} in an {@link Fn5}. + * + * @param curriedFn4 the curried fn4 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the output type + * @return the {@link Fn5} + */ + static Fn5 fn5(Fn4> curriedFn4) { + return (a, b, c, d, e) -> curriedFn4.apply(a, b, c, d).apply(e); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn6.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn6.java new file mode 100644 index 000000000..e81331b16 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn6.java @@ -0,0 +1,234 @@ +package com.jnape.palatable.lambda.functions; + +import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import com.jnape.palatable.lambda.functor.Applicative; + +import java.util.function.BiFunction; +import java.util.function.Function; + +import static com.jnape.palatable.lambda.functions.Fn7.fn7; + +/** + * A function taking six arguments. Defined in terms of {@link Fn5}, so similarly auto-curried. + * + * @param The first argument type + * @param The second argument type + * @param The third argument type + * @param The fourth argument type + * @param The fifth argument type + * @param The sixth argument type + * @param The return type + * @see Fn5 + */ +@FunctionalInterface +public interface Fn6 extends Fn5> { + + /** + * Invoke this function with the given arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @param d the fourth argument + * @param e the fifth argument + * @param f the sixth argument + * @return the result of the function application + */ + G apply(A a, B b, C c, D d, E e, F f); + + /** + * Partially apply this function by taking its first argument. + * + * @param a the first argument + * @return an {@link Fn5} that takes the remaining arguments and returns the result + */ + @Override + default Fn5 apply(A a) { + return (b, c, d, e, f) -> apply(a, b, c, d, e, f); + } + + /** + * Partially apply this function by taking its first two arguments. + * + * @param a the first argument + * @param b the second argument + * @return an {@link Fn4} that takes the remaining arguments and returns the result + */ + @Override + default Fn4 apply(A a, B b) { + return (c, d, e, f) -> apply(a, b, c, d, e, f); + } + + /** + * Partially apply this function by taking its first three arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @return an {@link Fn3} that takes remaining arguments and returns the result + */ + @Override + default Fn3 apply(A a, B b, C c) { + return (d, e, f) -> apply(a, b, c, d, e, f); + } + + /** + * Partially apply this function by taking its first four arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @param d the fourth argument + * @return an {@link Fn2} that takes the remaining arguments and returns the result + */ + @Override + default Fn2 apply(A a, B b, C c, D d) { + return (e, f) -> apply(a, b, c, d, e, f); + } + + /** + * Partially apply this function by taking its first five arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @param d the fourth argument + * @param e the fifth argument + * @return an {@link Fn1} that takes the remaining argument and returns the result + */ + @Override + default Fn1 apply(A a, B b, C c, D d, E e) { + return (f) -> apply(a, b, c, d, e, f); + } + + /** + * Flip the order of the first two arguments. + * + * @return an {@link Fn6} that takes the first and second arguments in reversed order + */ + @Override + default Fn6 flip() { + return (b, a, c, d, e, f) -> apply(a, b, c, d, e, f); + } + + /** + * Returns an {@link Fn5} that takes the first two arguments as a {@link Tuple2}<A, B> and the + * remaining arguments. + * + * @return an {@link Fn5} taking a {@link Tuple2} and the remaining arguments + */ + @Override + default Fn5, C, D, E, F, G> uncurry() { + return (ab, c, d, e, f) -> apply(ab._1(), ab._2(), c, d, e, f); + } + + @Override + default Fn6 discardR(Applicative> appB) { + return fn6(Fn5.super.discardR(appB)); + } + + @Override + default Fn6 diMapL(Function fn) { + return fn6(Fn5.super.diMapL(fn)); + } + + @Override + default Fn6 contraMap(Function fn) { + return fn6(Fn5.super.contraMap(fn)); + } + + @Override + default Fn7 compose(BiFunction before) { + return fn7(Fn5.super.compose(before)); + } + + @Override + default Fn7 compose(Fn2 before) { + return fn7(Fn5.super.compose(before)); + } + + /** + * Static factory method for wrapping a curried {@link Fn1} in an {@link Fn6}. + * + * @param curriedFn1 the curried fn1 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the sixth input argument type + * @param the output type + * @return the {@link Fn6} + */ + static Fn6 fn6(Fn1> curriedFn1) { + return (a, b, c, d, e, f) -> curriedFn1.apply(a).apply(b, c, d, e, f); + } + + /** + * Static factory method for wrapping a curried {@link Fn2} in an {@link Fn6}. + * + * @param curriedFn2 the curried fn2 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the sixth input argument type + * @param the output type + * @return the {@link Fn6} + */ + static Fn6 fn6(Fn2> curriedFn2) { + return (a, b, c, d, e, f) -> curriedFn2.apply(a, b).apply(c, d, e, f); + } + + /** + * Static factory method for wrapping a curried {@link Fn3} in an {@link Fn6}. + * + * @param curriedFn3 the curried fn3 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the sixth input argument type + * @param the output type + * @return the {@link Fn6} + */ + static Fn6 fn6(Fn3> curriedFn3) { + return (a, b, c, d, e, f) -> curriedFn3.apply(a, b, c).apply(d, e, f); + } + + /** + * Static factory method for wrapping a curried {@link Fn4} in an {@link Fn6}. + * + * @param curriedFn4 the curried fn4 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the sixth input argument type + * @param the output type + * @return the {@link Fn6} + */ + static Fn6 fn6(Fn4> curriedFn4) { + return (a, b, c, d, e, f) -> curriedFn4.apply(a, b, c, d).apply(e, f); + } + + /** + * Static factory method for wrapping a curried {@link Fn5} in an {@link Fn6}. + * + * @param curriedFn5 the curried fn4 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the sixth input argument type + * @param the output type + * @return the {@link Fn6} + */ + static Fn6 fn6(Fn5> curriedFn5) { + return (a, b, c, d, e, f) -> curriedFn5.apply(a, b, c, d, e).apply(f); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn7.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn7.java new file mode 100644 index 000000000..20ad5d8e5 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn7.java @@ -0,0 +1,275 @@ +package com.jnape.palatable.lambda.functions; + +import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import com.jnape.palatable.lambda.functor.Applicative; + +import java.util.function.BiFunction; +import java.util.function.Function; + +import static com.jnape.palatable.lambda.functions.Fn8.fn8; + +/** + * A function taking six arguments. Defined in terms of {@link Fn6}, so similarly auto-curried. + * + * @param The first argument type + * @param The second argument type + * @param The third argument type + * @param The fourth argument type + * @param The fifth argument type + * @param The sixth argument type + * @param The seventh argument type + * @param The return type + * @see Fn6 + */ +@FunctionalInterface +public interface Fn7 extends Fn6> { + + /** + * Invoke this function with the given arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @param d the fourth argument + * @param e the fifth argument + * @param f the sixth argument + * @param g the seventh argument + * @return the result of the function application + */ + H apply(A a, B b, C c, D d, E e, F f, G g); + + /** + * Partially apply this function by taking its first argument. + * + * @param a the first argument + * @return an {@link Fn6} that takes the remaining arguments and returns the result + */ + @Override + default Fn6 apply(A a) { + return (b, c, d, e, f, g) -> apply(a, b, c, d, e, f, g); + } + + /** + * Partially apply this function by taking its first two arguments. + * + * @param a the first argument + * @param b the second argument + * @return an {@link Fn5} that takes the remaining arguments and returns the result + */ + @Override + default Fn5 apply(A a, B b) { + return (c, d, e, f, g) -> apply(a, b, c, d, e, f, g); + } + + /** + * Partially apply this function by taking its first three arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @return an {@link Fn4} that takes remaining arguments and returns the result + */ + @Override + default Fn4 apply(A a, B b, C c) { + return (d, e, f, g) -> apply(a, b, c, d, e, f, g); + } + + /** + * Partially apply this function by taking its first four arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @param d the fourth argument + * @return an {@link Fn3} that takes the remaining arguments and returns the result + */ + @Override + default Fn3 apply(A a, B b, C c, D d) { + return (e, f, g) -> apply(a, b, c, d, e, f, g); + } + + /** + * Partially apply this function by taking its first five arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @param d the fourth argument + * @param e the fifth argument + * @return an {@link Fn2} that takes the remaining arguments and returns the result + */ + @Override + default Fn2 apply(A a, B b, C c, D d, E e) { + return (f, g) -> apply(a, b, c, d, e, f, g); + } + + /** + * Partially apply this function by taking its first six arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @param d the fourth argument + * @param e the fifth argument + * @param f the sixth argument + * @return an {@link Fn1} that takes the remaining argument and returns the result + */ + @Override + default Fn1 apply(A a, B b, C c, D d, E e, F f) { + return (g) -> apply(a, b, c, d, e, f, g); + } + + /** + * Flip the order of the first two arguments. + * + * @return an {@link Fn7} that takes the first and second arguments in reversed order + */ + @Override + default Fn7 flip() { + return (b, a, c, d, e, f, g) -> apply(a, b, c, d, e, f, g); + } + + /** + * Returns an {@link Fn6} that takes the first two arguments as a {@link Tuple2}<A, B> and the + * remaining arguments. + * + * @return an {@link Fn6} taking a {@link Tuple2} and the remaining arguments + */ + @Override + default Fn6, C, D, E, F, G, H> uncurry() { + return (ab, c, d, e, f, g) -> apply(ab._1(), ab._2(), c, d, e, f, g); + } + + @Override + default Fn7 discardR(Applicative> appB) { + return fn7(Fn6.super.discardR(appB)); + } + + @Override + default Fn7 diMapL(Function fn) { + return fn7(Fn6.super.diMapL(fn)); + } + + @Override + default Fn7 contraMap(Function fn) { + return fn7(Fn6.super.contraMap(fn)); + } + + @Override + default Fn8 compose(BiFunction before) { + return fn8(Fn6.super.compose(before)); + } + + @Override + default Fn8 compose(Fn2 before) { + return fn8(Fn6.super.compose(before)); + } + + /** + * Static factory method for wrapping a curried {@link Fn1} in an {@link Fn7}. + * + * @param curriedFn1 the curried fn1 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the sixth input argument type + * @param the seventh input argument type + * @param the output type + * @return the {@link Fn7} + */ + static Fn7 fn7(Fn1> curriedFn1) { + return (a, b, c, d, e, f, g) -> curriedFn1.apply(a).apply(b, c, d, e, f, g); + } + + /** + * Static factory method for wrapping a curried {@link Fn2} in an {@link Fn7}. + * + * @param curriedFn2 the curried fn2 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the sixth input argument type + * @param the seventh input argument type + * @param the output type + * @return the {@link Fn7} + */ + static Fn7 fn7(Fn2> curriedFn2) { + return (a, b, c, d, e, f, g) -> curriedFn2.apply(a, b).apply(c, d, e, f, g); + } + + /** + * Static factory method for wrapping a curried {@link Fn3} in an {@link Fn7}. + * + * @param curriedFn3 the curried fn3 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the sixth input argument type + * @param the seventh input argument type + * @param the output type + * @return the {@link Fn7} + */ + static Fn7 fn7(Fn3> curriedFn3) { + return (a, b, c, d, e, f, g) -> curriedFn3.apply(a, b, c).apply(d, e, f, g); + } + + /** + * Static factory method for wrapping a curried {@link Fn4} in an {@link Fn7}. + * + * @param curriedFn4 the curried fn4 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the sixth input argument type + * @param the seventh input argument type + * @param the output type + * @return the {@link Fn7} + */ + static Fn7 fn7(Fn4> curriedFn4) { + return (a, b, c, d, e, f, g) -> curriedFn4.apply(a, b, c, d).apply(e, f, g); + } + + /** + * Static factory method for wrapping a curried {@link Fn5} in an {@link Fn7}. + * + * @param curriedFn5 the curried fn4 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the sixth input argument type + * @param the seventh input argument type + * @param the output type + * @return the {@link Fn7} + */ + static Fn7 fn7(Fn5> curriedFn5) { + return (a, b, c, d, e, f, g) -> curriedFn5.apply(a, b, c, d, e).apply(f, g); + } + + /** + * Static factory method for wrapping a curried {@link Fn6} in an {@link Fn7}. + * + * @param curriedFn6 the curried fn4 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the sixth input argument type + * @param the seventh input argument type + * @param the output type + * @return the {@link Fn7} + */ + static Fn7 fn7(Fn6> curriedFn6) { + return (a, b, c, d, e, f, g) -> curriedFn6.apply(a, b, c, d, e, f).apply(g); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/Fn8.java b/src/main/java/com/jnape/palatable/lambda/functions/Fn8.java new file mode 100644 index 000000000..6f80beb9f --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/Fn8.java @@ -0,0 +1,325 @@ +package com.jnape.palatable.lambda.functions; + +import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import com.jnape.palatable.lambda.functor.Applicative; + +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * A function taking six arguments. Defined in terms of {@link Fn7}, so similarly auto-curried. + * + * @param The first argument type + * @param The second argument type + * @param The third argument type + * @param The fourth argument type + * @param The fifth argument type + * @param The sixth argument type + * @param The seventh argument type + * @param The eighth argument type + * @param The return type + * @see Fn7 + */ +@FunctionalInterface +public interface Fn8 extends Fn7> { + + /** + * Invoke this function with the given arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @param d the fourth argument + * @param e the fifth argument + * @param f the sixth argument + * @param g the seventh argument + * @param h the eighth argument + * @return the result of the function application + */ + I apply(A a, B b, C c, D d, E e, F f, G g, H h); + + /** + * Partially apply this function by taking its first argument. + * + * @param a the first argument + * @return an {@link Fn7} that takes the remaining arguments and returns the result + */ + @Override + default Fn7 apply(A a) { + return (b, c, d, e, f, g, h) -> apply(a, b, c, d, e, f, g, h); + } + + /** + * Partially apply this function by taking its first two arguments. + * + * @param a the first argument + * @param b the second argument + * @return an {@link Fn6} that takes the remaining arguments and returns the result + */ + @Override + default Fn6 apply(A a, B b) { + return (c, d, e, f, g, h) -> apply(a, b, c, d, e, f, g, h); + } + + /** + * Partially apply this function by taking its first three arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @return an {@link Fn5} that takes remaining arguments and returns the result + */ + @Override + default Fn5 apply(A a, B b, C c) { + return (d, e, f, g, h) -> apply(a, b, c, d, e, f, g, h); + } + + /** + * Partially apply this function by taking its first four arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @param d the fourth argument + * @return an {@link Fn4} that takes the remaining arguments and returns the result + */ + @Override + default Fn4 apply(A a, B b, C c, D d) { + return (e, f, g, h) -> apply(a, b, c, d, e, f, g, h); + } + + /** + * Partially apply this function by taking its first five arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @param d the fourth argument + * @param e the fifth argument + * @return an {@link Fn3} that takes the remaining arguments and returns the result + */ + @Override + default Fn3 apply(A a, B b, C c, D d, E e) { + return (f, g, h) -> apply(a, b, c, d, e, f, g, h); + } + + /** + * Partially apply this function by taking its first six arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @param d the fourth argument + * @param e the fifth argument + * @param f the sixth argument + * @return an {@link Fn2} that takes the remaining arguments and returns the result + */ + @Override + default Fn2 apply(A a, B b, C c, D d, E e, F f) { + return (g, h) -> apply(a, b, c, d, e, f, g, h); + } + + /** + * Partially apply this function by taking its first seven arguments. + * + * @param a the first argument + * @param b the second argument + * @param c the third argument + * @param d the fourth argument + * @param e the fifth argument + * @param f the sixth argument + * @param g the seventh argument + * @return an {@link Fn1} that takes the remaining argument and returns the result + */ + @Override + default Fn1 apply(A a, B b, C c, D d, E e, F f, G g) { + return (h) -> apply(a, b, c, d, e, f, g, h); + } + + /** + * Flip the order of the first two arguments. + * + * @return an {@link Fn8} that takes the first and second arguments in reversed order + */ + @Override + default Fn8 flip() { + return (b, a, c, d, e, f, g, h) -> apply(a, b, c, d, e, f, g, h); + } + + /** + * Returns an {@link Fn7} that takes the first two arguments as a {@link Tuple2}<A, B> and the + * remaining arguments. + * + * @return an {@link Fn7} taking a {@link Tuple2} and the remaining arguments + */ + @Override + default Fn7, C, D, E, F, G, H, I> uncurry() { + return (ab, c, d, e, f, g, h) -> apply(ab._1(), ab._2(), c, d, e, f, g, h); + } + + @Override + default Fn8 discardR(Applicative> appB) { + return fn8(Fn7.super.discardR(appB)); + } + + @Override + default Fn8 diMapL(Function fn) { + return fn8(Fn7.super.diMapL(fn)); + } + + @Override + default Fn8 contraMap(Function fn) { + return fn8(Fn7.super.contraMap(fn)); + } + + @Override + default Fn8> compose( + BiFunction before) { + return Fn7.super.compose(before); + } + + @Override + default Fn8> compose(Fn2 before) { + return Fn7.super.compose(before); + } + + /** + * Static factory method for wrapping a curried {@link Fn1} in an {@link Fn8}. + * + * @param curriedFn1 the curried fn1 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the sixth input argument type + * @param the seventh input argument type + * @param the eighth input argument type + * @param the output type + * @return the {@link Fn8} + */ + static Fn8 fn8( + Fn1> curriedFn1) { + return (a, b, c, d, e, f, g, h) -> curriedFn1.apply(a).apply(b, c, d, e, f, g, h); + } + + /** + * Static factory method for wrapping a curried {@link Fn2} in an {@link Fn8}. + * + * @param curriedFn2 the curried fn2 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the sixth input argument type + * @param the seventh input argument type + * @param the eighth input argument type + * @param the output type + * @return the {@link Fn8} + */ + static Fn8 fn8( + Fn2> curriedFn2) { + return (a, b, c, d, e, f, g, h) -> curriedFn2.apply(a, b).apply(c, d, e, f, g, h); + } + + /** + * Static factory method for wrapping a curried {@link Fn3} in an {@link Fn8}. + * + * @param curriedFn3 the curried fn3 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the sixth input argument type + * @param the seventh input argument type + * @param the eighth input argument type + * @param the output type + * @return the {@link Fn8} + */ + static Fn8 fn8( + Fn3> curriedFn3) { + return (a, b, c, d, e, f, g, h) -> curriedFn3.apply(a, b, c).apply(d, e, f, g, h); + } + + /** + * Static factory method for wrapping a curried {@link Fn4} in an {@link Fn8}. + * + * @param curriedFn4 the curried fn4 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the sixth input argument type + * @param the seventh input argument type + * @param the eighth input argument type + * @param the output type + * @return the {@link Fn8} + */ + static Fn8 fn8( + Fn4> curriedFn4) { + return (a, b, c, d, e, f, g, h) -> curriedFn4.apply(a, b, c, d).apply(e, f, g, h); + } + + /** + * Static factory method for wrapping a curried {@link Fn5} in an {@link Fn8}. + * + * @param curriedFn5 the curried fn4 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the sixth input argument type + * @param the seventh input argument type + * @param the eighth input argument type + * @param the output type + * @return the {@link Fn8} + */ + static Fn8 fn8( + Fn5> curriedFn5) { + return (a, b, c, d, e, f, g, h) -> curriedFn5.apply(a, b, c, d, e).apply(f, g, h); + } + + /** + * Static factory method for wrapping a curried {@link Fn6} in an {@link Fn8}. + * + * @param curriedFn6 the curried fn4 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the sixth input argument type + * @param the seventh input argument type + * @param the eighth input argument type + * @param the output type + * @return the {@link Fn8} + */ + static Fn8 fn8( + Fn6> curriedFn6) { + return (a, b, c, d, e, f, g, h) -> curriedFn6.apply(a, b, c, d, e, f).apply(g, h); + } + + /** + * Static factory method for wrapping a curried {@link Fn7} in an {@link Fn8}. + * + * @param curriedFn7 the curried fn4 to adapt + * @param the first input argument type + * @param the second input argument type + * @param the third input argument type + * @param the fourth input argument type + * @param the fifth input argument type + * @param the sixth input argument type + * @param the seventh input argument type + * @param the eighth input argument type + * @param the output type + * @return the {@link Fn8} + */ + static Fn8 fn8( + Fn7> curriedFn7) { + return (a, b, c, d, e, f, g, h) -> curriedFn7.apply(a, b, c, d, e, f, g).apply(h); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java index ecf363e27..64179b33a 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple5Test.java @@ -77,6 +77,12 @@ public void randomAccess() { verifyNoMoreInteractions(spiedTail); } + @Test + public void into() { + Tuple5 tuple = tuple("foo", 1, 2.0d, false, 3f); + assertEquals("foo12.0false3.0", tuple.into((s, i, d, b, f) -> s + i + d + b + f)); + } + @Test public void fill() { assertEquals(tuple("foo", "foo", "foo", "foo", "foo"), Tuple5.fill("foo")); diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java index b86a683a6..ee2020c4d 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple6Test.java @@ -81,6 +81,12 @@ public void randomAccess() { verifyNoMoreInteractions(spiedTail); } + @Test + public void into() { + Tuple6 tuple = tuple("foo", 1, 2.0d, false, 3f, (short) 4); + assertEquals("foo12.0false3.04", tuple.into((s, i, d, b, f, sh) -> s + i + d + b + f + sh)); + } + @Test public void fill() { assertEquals(tuple("foo", "foo", "foo", "foo", "foo", "foo"), Tuple6.fill("foo")); diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java index d9fee2832..4025e470b 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple7Test.java @@ -89,6 +89,12 @@ public void fill() { assertEquals(tuple("foo", "foo", "foo", "foo", "foo", "foo", "foo"), Tuple7.fill("foo")); } + @Test + public void into() { + Tuple7 tuple = tuple("foo", 1, 2.0d, false, 3f, (short) 4, (byte) 5); + assertEquals("foo12.0false3.045", tuple.into((s, i, d, b, f, sh, by) -> s + i + d + b + f + sh + by)); + } + @Test public void zipPrecedence() { Tuple7 a = tuple("foo", 1, 2, 3, 4, 5, 6); diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java index 7044715b6..a127cbc08 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple8Test.java @@ -92,6 +92,12 @@ public void fill() { assertEquals(tuple("foo", "foo", "foo", "foo", "foo", "foo", "foo", "foo"), Tuple8.fill("foo")); } + @Test + public void into() { + Tuple8 tuple = tuple("foo", 1, 2.0d, false, 3f, (short) 4, (byte) 5, 6L); + assertEquals("foo12.0false3.0456", tuple.into((s, i, d, b, f, sh, by, l) -> s + i + d + b + f + sh + by + l)); + } + @Test public void zipPrecedence() { Tuple8 a = tuple("foo", 1, 2, 3, 4, 5, 6, 7); diff --git a/src/test/java/com/jnape/palatable/lambda/functions/Fn5Test.java b/src/test/java/com/jnape/palatable/lambda/functions/Fn5Test.java new file mode 100644 index 000000000..bb5d2021d --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/Fn5Test.java @@ -0,0 +1,48 @@ +package com.jnape.palatable.lambda.functions; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +public class Fn5Test { + + private static final Fn5 APPEND = + (s1, s2, s3, s4, s5) -> s1 + s2 + s3 + s4 + s5; + + @Test + public void canBePartiallyApplied() { + assertThat(APPEND.apply("a").apply("b").apply("c").apply("d").apply("e"), is("abcde")); + assertThat(APPEND.apply("a").apply("b").apply("c").apply("d", "e"), is("abcde")); + assertThat(APPEND.apply("a").apply("b").apply("c", "d", "e"), is("abcde")); + assertThat(APPEND.apply("a").apply("b", "c", "d", "e"), is("abcde")); + assertThat(APPEND.apply("a", "b", "c", "d", "e"), is("abcde")); + } + + @Test + public void flipsFirstAndSecondArgument() { + assertThat(APPEND.flip().apply("a", "b", "c", "d", "e"), is("bacde")); + } + + @Test + public void uncurries() { + assertThat(APPEND.uncurry().apply(tuple("a", "b"), "c", "d", "e"), is("abcde")); + } + + @Test + public void fn5() { + Fn1> fn1 = a -> (b, c, d, e) -> a + b + c + d + e; + assertEquals("abcde", Fn5.fn5(fn1).apply("a", "b", "c", "d", "e")); + + Fn2> fn2 = (a, b) -> (c, d, e) -> a + b + c + d + e; + assertEquals("abcde", Fn5.fn5(fn2).apply("a", "b", "c", "d", "e")); + + Fn3> fn3 = (a, b, c) -> (d, e) -> a + b + c + d + e; + assertEquals("abcde", Fn5.fn5(fn3).apply("a", "b", "c", "d", "e")); + + Fn4> fn4 = (a, b, c, d) -> (e) -> a + b + c + d + e; + assertEquals("abcde", Fn5.fn5(fn4).apply("a", "b", "c", "d", "e")); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/Fn6Test.java b/src/test/java/com/jnape/palatable/lambda/functions/Fn6Test.java new file mode 100644 index 000000000..e52b1b54f --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/Fn6Test.java @@ -0,0 +1,52 @@ +package com.jnape.palatable.lambda.functions; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +public class Fn6Test { + + private static final Fn6 APPEND = + (s1, s2, s3, s4, s5, s6) -> s1 + s2 + s3 + s4 + s5 + s6; + + @Test + public void canBePartiallyApplied() { + assertThat(APPEND.apply("a").apply("b").apply("c").apply("d").apply("e").apply("f"), is("abcdef")); + assertThat(APPEND.apply("a").apply("b").apply("c").apply("d").apply("e", "f"), is("abcdef")); + assertThat(APPEND.apply("a").apply("b").apply("c").apply("d", "e", "f"), is("abcdef")); + assertThat(APPEND.apply("a").apply("b").apply("c", "d", "e", "f"), is("abcdef")); + assertThat(APPEND.apply("a").apply("b", "c", "d", "e", "f"), is("abcdef")); + assertThat(APPEND.apply("a", "b", "c", "d", "e", "f"), is("abcdef")); + } + + @Test + public void flipsFirstAndSecondArgument() { + assertThat(APPEND.flip().apply("a", "b", "c", "d", "e", "f"), is("bacdef")); + } + + @Test + public void uncurries() { + assertThat(APPEND.uncurry().apply(tuple("a", "b"), "c", "d", "e", "f"), is("abcdef")); + } + + @Test + public void fn6() { + Fn1> fn1 = a -> (b, c, d, e, f) -> a + b + c + d + e + f; + assertEquals("abcdef", Fn6.fn6(fn1).apply("a", "b", "c", "d", "e", "f")); + + Fn2> fn2 = (a, b) -> (c, d, e, f) -> a + b + c + d + e + f; + assertEquals("abcdef", Fn6.fn6(fn2).apply("a", "b", "c", "d", "e", "f")); + + Fn3> fn3 = (a, b, c) -> (d, e, f) -> a + b + c + d + e + f; + assertEquals("abcdef", Fn6.fn6(fn3).apply("a", "b", "c", "d", "e", "f")); + + Fn4> fn4 = (a, b, c, d) -> (e, f) -> a + b + c + d + e + f; + assertEquals("abcdef", Fn6.fn6(fn4).apply("a", "b", "c", "d", "e", "f")); + + Fn5> fn5 = (a, b, c, d, e) -> (f) -> a + b + c + d + e + f; + assertEquals("abcdef", Fn6.fn6(fn5).apply("a", "b", "c", "d", "e", "f")); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/Fn7Test.java b/src/test/java/com/jnape/palatable/lambda/functions/Fn7Test.java new file mode 100644 index 000000000..9baf0fe0b --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/Fn7Test.java @@ -0,0 +1,56 @@ +package com.jnape.palatable.lambda.functions; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +public class Fn7Test { + + private static final Fn7 APPEND = + (s1, s2, s3, s4, s5, s6, s7) -> s1 + s2 + s3 + s4 + s5 + s6 + s7; + + @Test + public void canBePartiallyApplied() { + assertThat(APPEND.apply("a").apply("b").apply("c").apply("d").apply("e").apply("f").apply("g"), is("abcdefg")); + assertThat(APPEND.apply("a").apply("b").apply("c").apply("d").apply("e").apply("f", "g"), is("abcdefg")); + assertThat(APPEND.apply("a").apply("b").apply("c").apply("d").apply("e", "f", "g"), is("abcdefg")); + assertThat(APPEND.apply("a").apply("b").apply("c").apply("d", "e", "f", "g"), is("abcdefg")); + assertThat(APPEND.apply("a").apply("b").apply("c", "d", "e", "f", "g"), is("abcdefg")); + assertThat(APPEND.apply("a").apply("b", "c", "d", "e", "f", "g"), is("abcdefg")); + assertThat(APPEND.apply("a", "b", "c", "d", "e", "f", "g"), is("abcdefg")); + } + + @Test + public void flipsFirstAndSecondArgument() { + assertThat(APPEND.flip().apply("a", "b", "c", "d", "e", "f", "g"), is("bacdefg")); + } + + @Test + public void uncurries() { + assertThat(APPEND.uncurry().apply(tuple("a", "b"), "c", "d", "e", "f", "g"), is("abcdefg")); + } + + @Test + public void fn7() { + Fn1> fn1 = a -> (b, c, d, e, f, g) -> a + b + c + d + e + f + g; + assertEquals("abcdefg", Fn7.fn7(fn1).apply("a", "b", "c", "d", "e", "f", "g")); + + Fn2> fn2 = (a, b) -> (c, d, e, f, g) -> a + b + c + d + e + f + g; + assertEquals("abcdefg", Fn7.fn7(fn2).apply("a", "b", "c", "d", "e", "f", "g")); + + Fn3> fn3 = (a, b, c) -> (d, e, f, g) -> a + b + c + d + e + f + g; + assertEquals("abcdefg", Fn7.fn7(fn3).apply("a", "b", "c", "d", "e", "f", "g")); + + Fn4> fn4 = (a, b, c, d) -> (e, f, g) -> a + b + c + d + e + f + g; + assertEquals("abcdefg", Fn7.fn7(fn4).apply("a", "b", "c", "d", "e", "f", "g")); + + Fn5> fn5 = (a, b, c, d, e) -> (f, g) -> a + b + c + d + e + f + g; + assertEquals("abcdefg", Fn7.fn7(fn5).apply("a", "b", "c", "d", "e", "f", "g")); + + Fn6> fn6 = (a, b, c, d, e, f) -> (g) -> a + b + c + d + e + f + g; + assertEquals("abcdefg", Fn7.fn7(fn6).apply("a", "b", "c", "d", "e", "f", "g")); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/Fn8Test.java b/src/test/java/com/jnape/palatable/lambda/functions/Fn8Test.java new file mode 100644 index 000000000..ea220e2f8 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/Fn8Test.java @@ -0,0 +1,60 @@ +package com.jnape.palatable.lambda.functions; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +public class Fn8Test { + + private static final Fn8 APPEND = + (s1, s2, s3, s4, s5, s6, s7, s8) -> s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8; + + @Test + public void canBePartiallyApplied() { + assertThat(APPEND.apply("a").apply("b").apply("c").apply("d").apply("e").apply("f").apply("g").apply("h"), is("abcdefgh")); + assertThat(APPEND.apply("a").apply("b").apply("c").apply("d").apply("e").apply("f").apply("g", "h"), is("abcdefgh")); + assertThat(APPEND.apply("a").apply("b").apply("c").apply("d").apply("e").apply("f", "g", "h"), is("abcdefgh")); + assertThat(APPEND.apply("a").apply("b").apply("c").apply("d").apply("e", "f", "g", "h"), is("abcdefgh")); + assertThat(APPEND.apply("a").apply("b").apply("c").apply("d", "e", "f", "g", "h"), is("abcdefgh")); + assertThat(APPEND.apply("a").apply("b").apply("c", "d", "e", "f", "g", "h"), is("abcdefgh")); + assertThat(APPEND.apply("a").apply("b", "c", "d", "e", "f", "g", "h"), is("abcdefgh")); + assertThat(APPEND.apply("a", "b", "c", "d", "e", "f", "g", "h"), is("abcdefgh")); + } + + @Test + public void flipsFirstAndSecondArgument() { + assertThat(APPEND.flip().apply("a", "b", "c", "d", "e", "f", "g", "h"), is("bacdefgh")); + } + + @Test + public void uncurries() { + assertThat(APPEND.uncurry().apply(tuple("a", "b"), "c", "d", "e", "f", "g", "h"), is("abcdefgh")); + } + + @Test + public void fn8() { + Fn1> fn1 = a -> (b, c, d, e, f, g, h) -> a + b + c + d + e + f + g + h; + assertEquals("abcdefgh", Fn8.fn8(fn1).apply("a", "b", "c", "d", "e", "f", "g", "h")); + + Fn2> fn2 = (a, b) -> (c, d, e, f, g, h) -> a + b + c + d + e + f + g + h; + assertEquals("abcdefgh", Fn8.fn8(fn2).apply("a", "b", "c", "d", "e", "f", "g", "h")); + + Fn3> fn3 = (a, b, c) -> (d, e, f, g, h) -> a + b + c + d + e + f + g + h; + assertEquals("abcdefgh", Fn8.fn8(fn3).apply("a", "b", "c", "d", "e", "f", "g", "h")); + + Fn4> fn4 = (a, b, c, d) -> (e, f, g, h) -> a + b + c + d + e + f + g + h; + assertEquals("abcdefgh", Fn8.fn8(fn4).apply("a", "b", "c", "d", "e", "f", "g", "h")); + + Fn5> fn5 = (a, b, c, d, e) -> (f, g, h) -> a + b + c + d + e + f + g + h; + assertEquals("abcdefgh", Fn8.fn8(fn5).apply("a", "b", "c", "d", "e", "f", "g", "h")); + + Fn6> fn6 = (a, b, c, d, e, f) -> (g, h) -> a + b + c + d + e + f + g + h; + assertEquals("abcdefgh", Fn8.fn8(fn6).apply("a", "b", "c", "d", "e", "f", "g", "h")); + + Fn7> fn7 = (a, b, c, d, e, f, g) -> (h) -> a + b + c + d + e + f + g + h; + assertEquals("abcdefgh", Fn8.fn8(fn7).apply("a", "b", "c", "d", "e", "f", "g", "h")); + } +} From 6d7be1b4961959be255a2c33d88c48f48dd5f81b Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 19 Nov 2017 15:18:26 -0600 Subject: [PATCH 06/24] Removing methods and types deprecated in previous release --- CHANGELOG.md | 8 + .../jnape/palatable/lambda/adt/Either.java | 29 --- .../functions/builtin/fn2/Sequence.java | 18 -- .../lambda/lens/lenses/OptionalLens.java | 166 ------------------ .../traversable/TraversableIterable.java | 94 ---------- .../traversable/TraversableOptional.java | 80 --------- .../lambda/traversable/Traversables.java | 43 ----- 7 files changed, 8 insertions(+), 430 deletions(-) delete mode 100644 src/main/java/com/jnape/palatable/lambda/lens/lenses/OptionalLens.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/traversable/TraversableIterable.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/traversable/TraversableOptional.java delete mode 100644 src/main/java/com/jnape/palatable/lambda/traversable/Traversables.java diff --git a/CHANGELOG.md b/CHANGELOG.md index f95faaa37..5b2a1c652 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `Tuple5#into` - `Tuple6` through `Tuple8` +### Removed +- `Either#toOptional`, deprecated in previous release +- `Either#fromOptional`, deprecated in previous release +- `sequence` overloads supporting `Optional`, deprecated in previous release +- `OptionalLens`, deprecated in previous release +- `TraversableIterable`, deprecated in previous release +- `Traversables`, deprecated in previous release + ## [2.0.0] - 2017-11-13 ### Changed - ***Breaking Change***: `java.util.Optional` replaced with `Maybe` across the board 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 c00b3da6b..b305c99d7 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/Either.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/Either.java @@ -9,7 +9,6 @@ import com.jnape.palatable.lambda.traversable.Traversable; import java.util.Objects; -import java.util.Optional; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; @@ -244,18 +243,6 @@ public Applicative, App> traverse( r -> fn.apply(r).fmap(Either::right)); } - /** - * In the left case, returns an {@link Optional#empty}; otherwise, returns {@link Optional#ofNullable} around the - * right value. - * - * @return an Optional around the right value, or empty if left - * @deprecated in favor of {@link Either#toMaybe()} - */ - @Deprecated - public Optional toOptional() { - return toMaybe().toOptional(); - } - /** * In the left case, returns a {@link Maybe#nothing()}; otherwise, returns {@link Maybe#maybe} around the right * value. @@ -266,22 +253,6 @@ public Maybe toMaybe() { return projectB(); } - /** - * Convert an {@link Optional}<R> into an Either<L, R>, supplying the left value from - * leftFn in the case of {@link Optional#empty()}. - * - * @param optional the optional - * @param leftFn the supplier to use for left values - * @param the left parameter type - * @param the right parameter type - * @return a right value of the contained optional value, or a left value of leftFn's result - * @deprecated in favor of converting {@link Optional} to {@link Maybe}, then using {@link Either#fromMaybe} - */ - @Deprecated - public static Either fromOptional(Optional optional, Supplier leftFn) { - return fromMaybe(Maybe.fromOptional(optional), leftFn); - } - /** * Convert a {@link Maybe}<R> into an Either<L, R>, supplying the left value from * leftFn in the case of {@link Maybe#nothing()}. diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Sequence.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Sequence.java index 8fda2a962..3232736e7 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Sequence.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Sequence.java @@ -1,13 +1,11 @@ package com.jnape.palatable.lambda.functions.builtin.fn2; -import com.jnape.palatable.lambda.adt.Maybe; import com.jnape.palatable.lambda.functions.Fn1; import com.jnape.palatable.lambda.functions.Fn2; import com.jnape.palatable.lambda.functor.Applicative; import com.jnape.palatable.lambda.traversable.LambdaIterable; import com.jnape.palatable.lambda.traversable.Traversable; -import java.util.Optional; import java.util.function.Function; import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; @@ -72,25 +70,9 @@ TravApp extends Traversable, Trav>> AppTrav sequen .fmap(LambdaIterable::unwrap); } - @SuppressWarnings("unchecked") - @Deprecated - public static , App>, OptionalApp extends Optional>> Fn1, ? extends AppOptional>, AppOptional> sequence( - OptionalApp optionalApp) { - return pure -> (AppOptional) sequence(Maybe.fromOptional(optionalApp), x -> pure.apply(((Maybe) x).toOptional()) - .fmap(Maybe::fromOptional)) - .fmap(Maybe::toOptional); - } - public static , App>, IterableApp extends Iterable>> AppIterable sequence(IterableApp iterableApp, Function, ? extends AppIterable> pure) { return Sequence.sequence(iterableApp).apply(pure); } - - @Deprecated - public static , App>, - OptionalApp extends Optional>> AppOptional sequence(OptionalApp optionalApp, - Function, ? extends AppOptional> pure) { - return Sequence.sequence(optionalApp).apply(pure); - } } diff --git a/src/main/java/com/jnape/palatable/lambda/lens/lenses/OptionalLens.java b/src/main/java/com/jnape/palatable/lambda/lens/lenses/OptionalLens.java deleted file mode 100644 index 79baafcb8..000000000 --- a/src/main/java/com/jnape/palatable/lambda/lens/lenses/OptionalLens.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.jnape.palatable.lambda.lens.lenses; - -import com.jnape.palatable.lambda.adt.Maybe; -import com.jnape.palatable.lambda.lens.Lens; - -import java.util.Optional; - -import static com.jnape.palatable.lambda.lens.Lens.simpleLens; - -/** - * Lenses that operate on {@link Optional}s. - * - * @deprecated in favor of converting {@link Optional} to {@link Maybe} and using {@link MaybeLens} analogs - */ -@Deprecated -public final class OptionalLens { - - private OptionalLens() { - } - - /** - * Convenience static factory method for creating a lens that focuses on a value as an {@link Optional}. - * - * @param the value type - * @return a lens that focuses on the value as an Optional - * @deprecated in favor of {@link MaybeLens#asMaybe} - */ - @Deprecated - public static Lens.Simple> asOptional() { - return simpleLens(Optional::ofNullable, (v, optV) -> optV.orElse(v)); - } - - /** - * Given a lens and a default S, lift S into Optional. - * - * @param lens the lens - * @param defaultS the S to use if an empty Optional value is given - * @param the type of the "larger" value for reading - * @param the type of the "larger" value for putting - * @param the type of the "smaller" value that is read - * @param the type of the "smaller" update value - * @return the lens with S lifted - * @deprecated in favor of {@link MaybeLens#liftS} - */ - @Deprecated - public static Lens, T, A, B> liftS(Lens lens, S defaultS) { - return lens.mapS(optS -> optS.orElse(defaultS)); - } - - /** - * Given a lens, lift T into Optional. - * - * @param lens the lens - * @param the type of the "larger" value for reading - * @param the type of the "larger" value for putting - * @param the type of the "smaller" value that is read - * @param the type of the "smaller" update value - * @return the lens with T lifted - * @deprecated in favor of {@link MaybeLens#liftT} - */ - @Deprecated - public static Lens, A, B> liftT(Lens lens) { - return lens.mapT(Optional::ofNullable); - } - - /** - * Given a lens, lift A into Optional. - * - * @param lens the lens - * @param the type of the "larger" value for reading - * @param the type of the "larger" value for putting - * @param the type of the "smaller" value that is read - * @param the type of the "smaller" update value - * @return the lens with A lifted - * @deprecated in favor of {@link MaybeLens#liftA} - */ - @Deprecated - public static Lens, B> liftA(Lens lens) { - return lens.mapA(Optional::ofNullable); - } - - /** - * Given a lens and a default B, lift B into Optional. - * - * @param lens the lens - * @param defaultB the B to use if an empty Optional value is given - * @param the type of the "larger" value for reading - * @param the type of the "larger" value for putting - * @param the type of the "smaller" value that is read - * @param the type of the "smaller" update value - * @return the lens with B lifted - * @deprecated in favor of {@link MaybeLens#liftB} - */ - @Deprecated - public static Lens> liftB(Lens lens, B defaultB) { - return lens.mapB(optB -> optB.orElse(defaultB)); - } - - /** - * Given a lens with S lifted into Optional, flatten S back down. - * - * @param lens the lens - * @param the type of the "larger" value for reading - * @param the type of the "larger" value for putting - * @param the type of the "smaller" value that is read - * @param the type of the "smaller" update value - * @return the lens with S flattened - * @deprecated in favor of {@link MaybeLens#unLiftS} - */ - @Deprecated - public static Lens unLiftS(Lens, T, A, B> lens) { - return lens.mapS(Optional::ofNullable); - } - - /** - * Given a lens with T lifted into Optional and a default T, flatten T back - * down. - * - * @param lens the lens - * @param defaultT the T to use if lens produces an empty Optional - * @param the type of the "larger" value for reading - * @param the type of the "larger" value for putting - * @param the type of the "smaller" value that is read - * @param the type of the "smaller" update value - * @return the lens with T flattened - * @deprecated in favor of {@link MaybeLens#unLiftT} - */ - @Deprecated - public static Lens unLiftT(Lens, A, B> lens, T defaultT) { - return lens.mapT(optT -> optT.orElse(defaultT)); - } - - /** - * Given a lens with A lifted into Optional and a default A, flatten A back - * down. - * - * @param lens the lens - * @param defaultA the A to use if lens produces an empty Optional - * @param the type of the "larger" value for reading - * @param the type of the "larger" value for putting - * @param the type of the "smaller" value that is read - * @param the type of the "smaller" update value - * @return the lens with A flattened - * @deprecated in favor of {@link MaybeLens#unLiftA} - */ - @Deprecated - public static Lens unLiftA(Lens, B> lens, A defaultA) { - return lens.mapA(optA -> optA.orElse(defaultA)); - } - - /** - * Given a lens with B lifted, flatten B back down. - * - * @param lens the lens - * @param the type of the "larger" value for reading - * @param the type of the "larger" value for putting - * @param the type of the "smaller" value that is read - * @param the type of the "smaller" update value - * @return the lens with B flattened - * @deprecated in favor of {@link MaybeLens#unLiftB} - */ - @Deprecated - public static Lens unLiftB(Lens> lens) { - return lens.mapB(Optional::ofNullable); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/traversable/TraversableIterable.java b/src/main/java/com/jnape/palatable/lambda/traversable/TraversableIterable.java deleted file mode 100644 index 4c71cc589..000000000 --- a/src/main/java/com/jnape/palatable/lambda/traversable/TraversableIterable.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.jnape.palatable.lambda.traversable; - -import com.jnape.palatable.lambda.functor.Applicative; - -import java.util.Iterator; -import java.util.Objects; -import java.util.function.Function; - -import static com.jnape.palatable.lambda.functions.builtin.fn2.Cons.cons; -import static com.jnape.palatable.lambda.functions.builtin.fn2.Map.map; -import static com.jnape.palatable.lambda.functions.builtin.fn3.FoldRight.foldRight; -import static java.util.Collections.emptyList; - -/** - * Wrap an {@link Iterable} in a {@link Traversable} such that {@link Traversable#traverse(Function, Function)} applies - * its computation against each element of the wrapped {@link Iterable}. Returns the result of pure if the - * wrapped {@link Iterable} is empty. - * - * @param the Iterable element type - * @deprecated in favor of {@link LambdaIterable} - */ -@Deprecated -public final class TraversableIterable implements Traversable { - private final Iterable as; - - @SuppressWarnings("unchecked") - private TraversableIterable(Iterable as) { - this.as = (Iterable) as; - } - - /** - * Unwrap the underlying {@link Iterable}. - * - * @return the wrapped Iterable - */ - public Iterable unwrap() { - return as; - } - - @Override - public TraversableIterable fmap(Function fn) { - return wrap(map(fn, as)); - } - - @Override - public Applicative, App> traverse( - Function> fn, - Function, ? extends Applicative, App>> pure) { - return foldRight((a, appTrav) -> appTrav.zip(fn.apply(a).fmap(b -> bs -> TraversableIterable.wrap(cons(b, bs.unwrap())))), - pure.apply(TraversableIterable.empty()).fmap(ti -> (TraversableIterable) ti), - as); - } - - @Override - public boolean equals(Object other) { - if (other instanceof TraversableIterable) { - Iterator xs = as.iterator(); - Iterator ys = ((TraversableIterable) other).as.iterator(); - - while (xs.hasNext() && ys.hasNext()) - if (!Objects.equals(xs.next(), ys.next())) - return false; - - return xs.hasNext() == ys.hasNext(); - } - return false; - } - - @Override - public int hashCode() { - return Objects.hash(as); - } - - /** - * Wrap an {@link Iterable} in a TraversableIterable. - * - * @param as the Iterable - * @param the Iterable element type - * @return the Iterable wrapped in a TraversableIterable - */ - public static TraversableIterable wrap(Iterable as) { - return new TraversableIterable<>(as); - } - - /** - * Construct an empty TraversableIterable by wrapping {@link java.util.Collections#emptyList()}. - * - * @param the Iterable element type - * @return a TraversableIterable wrapping Collections.emptyList() - */ - public static TraversableIterable empty() { - return wrap(emptyList()); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/traversable/TraversableOptional.java b/src/main/java/com/jnape/palatable/lambda/traversable/TraversableOptional.java deleted file mode 100644 index c8f2ab62b..000000000 --- a/src/main/java/com/jnape/palatable/lambda/traversable/TraversableOptional.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.jnape.palatable.lambda.traversable; - -import com.jnape.palatable.lambda.adt.Maybe; -import com.jnape.palatable.lambda.functor.Applicative; - -import java.util.Objects; -import java.util.Optional; -import java.util.function.Function; - -/** - * Wrap an {@link Optional} in a {@link Traversable} such that {@link Traversable#traverse(Function, Function)} applies - * its computation against the value wrapped by the wrapped {@link Optional} (if the {@link Optional} is present). - * Returns the result of pure if the wrapped {@link Optional} is empty. - * - * @param the Optional parameter type - * @deprecated in favor of {@link Maybe} - */ -@Deprecated -public final class TraversableOptional implements Traversable { - private final Optional delegate; - - @SuppressWarnings("unchecked") - private TraversableOptional(Optional delegate) { - this.delegate = (Optional) delegate; - } - - /** - * Unwrap the underlying {@link Optional}. - * - * @return the wrapped Optional - */ - public Optional unwrap() { - return delegate; - } - - @Override - public TraversableOptional fmap(Function fn) { - return new TraversableOptional<>(delegate.map(fn)); - } - - @Override - public Applicative, App> traverse( - Function> fn, - Function, ? extends Applicative, App>> pure) { - return fmap(fn).delegate.map(app -> app.fmap(Optional::of).fmap(TraversableOptional::wrap)) - .orElseGet(() -> pure.apply(TraversableOptional.empty()).fmap(x -> (TraversableOptional) x)); - } - - @Override - public boolean equals(Object other) { - return other instanceof TraversableOptional - && Objects.equals(delegate, ((TraversableOptional) other).delegate); - } - - @Override - public int hashCode() { - return Objects.hash(delegate); - } - - /** - * Wrap an {@link Optional} in a TraversableOptional. - * - * @param optional the Optional - * @param the Optional parameter type - * @return the Optional wrapped in a TraversableOptional - */ - public static TraversableOptional wrap(Optional optional) { - return new TraversableOptional<>(optional); - } - - /** - * Construct an empty TraversableOptional by wrapping {@link Optional#empty()}. - * - * @param the optional parameter type - * @return a TraversableOptional wrapping Optional.empty() - */ - public static TraversableOptional empty() { - return TraversableOptional.wrap(Optional.empty()); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/traversable/Traversables.java b/src/main/java/com/jnape/palatable/lambda/traversable/Traversables.java deleted file mode 100644 index efac80658..000000000 --- a/src/main/java/com/jnape/palatable/lambda/traversable/Traversables.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.jnape.palatable.lambda.traversable; - -import com.jnape.palatable.lambda.adt.Maybe; - -import java.util.Optional; - -/** - * Static factory methods for adapting core JDK types to {@link Traversable}. - * - * @deprecated in favor of {@link LambdaIterable} and {@link Maybe}. - */ -@Deprecated -public final class Traversables { - - private Traversables() { - } - - /** - * Wrap an {@link Iterable} in a {@link Traversable}. - * - * @param as the Iterable - * @param the Iterable element type - * @return a Traversable wrapper around as - * @deprecated in favor of {@link LambdaIterable#wrap(Iterable)} - */ - @Deprecated - public static TraversableIterable traversable(Iterable as) { - return TraversableIterable.wrap(as); - } - - /** - * Wrap an {@link Optional} in a {@link Traversable}. - * - * @param opt the Optional - * @param the Optional type - * @return a Traversable wrapper around opt - * @deprecated in favor of {@link Maybe#fromOptional(Optional)} - */ - @Deprecated - public static TraversableOptional traversable(Optional opt) { - return TraversableOptional.wrap(opt); - } -} From b3b99ef29e046ba7600d82c5f7b4a7d1f99d790f Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 26 Nov 2017 13:29:20 -0600 Subject: [PATCH 07/24] Adding CoProduct6-8 and Choice6-8; cleaning up java docs --- README.md | 2 +- .../palatable/lambda/adt/choice/Choice2.java | 22 +- .../palatable/lambda/adt/choice/Choice3.java | 33 +- .../palatable/lambda/adt/choice/Choice4.java | 51 +- .../palatable/lambda/adt/choice/Choice5.java | 79 +-- .../palatable/lambda/adt/choice/Choice6.java | 399 ++++++++++++++ .../palatable/lambda/adt/choice/Choice7.java | 465 ++++++++++++++++ .../palatable/lambda/adt/choice/Choice8.java | 521 ++++++++++++++++++ .../lambda/adt/coproduct/CoProduct2.java | 11 +- .../lambda/adt/coproduct/CoProduct3.java | 14 +- .../lambda/adt/coproduct/CoProduct4.java | 12 +- .../lambda/adt/coproduct/CoProduct5.java | 33 +- .../lambda/adt/coproduct/CoProduct6.java | 179 ++++++ .../lambda/adt/coproduct/CoProduct7.java | 195 +++++++ .../lambda/adt/coproduct/CoProduct8.java | 192 +++++++ .../lambda/adt/choice/Choice5Test.java | 9 + .../lambda/adt/choice/Choice6Test.java | 73 +++ .../lambda/adt/choice/Choice7Test.java | 78 +++ .../lambda/adt/choice/Choice8Test.java | 71 +++ .../lambda/adt/coproduct/CoProduct3Test.java | 22 +- .../lambda/adt/coproduct/CoProduct4Test.java | 87 ++- .../lambda/adt/coproduct/CoProduct5Test.java | 90 +-- .../lambda/adt/coproduct/CoProduct6Test.java | 148 +++++ .../lambda/adt/coproduct/CoProduct7Test.java | 173 ++++++ .../lambda/adt/coproduct/CoProduct8Test.java | 180 ++++++ 25 files changed, 2884 insertions(+), 255 deletions(-) create mode 100644 src/main/java/com/jnape/palatable/lambda/adt/choice/Choice6.java create mode 100644 src/main/java/com/jnape/palatable/lambda/adt/choice/Choice7.java create mode 100644 src/main/java/com/jnape/palatable/lambda/adt/choice/Choice8.java create mode 100644 src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct6.java create mode 100644 src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct7.java create mode 100644 src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct8.java create mode 100644 src/test/java/com/jnape/palatable/lambda/adt/choice/Choice6Test.java create mode 100644 src/test/java/com/jnape/palatable/lambda/adt/choice/Choice7Test.java create mode 100644 src/test/java/com/jnape/palatable/lambda/adt/choice/Choice8Test.java create mode 100644 src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct6Test.java create mode 100644 src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct7Test.java create mode 100644 src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct8Test.java diff --git a/README.md b/README.md index 97c7ff6a0..3049495b0 100644 --- a/README.md +++ b/README.md @@ -579,7 +579,7 @@ CoProduct2 coProduct2 = Choice2.a("string"); CoProduct3 coProduct3 = coProduct2.diverge(); // still just the coProduct2 value, adapted to the coProduct3 shape ``` -There are `CoProduct` and `Choice` specializations for type unions of up to 5 different types: `CoProduct2` through `CoProduct5`, and `Choice2` through `Choice5`, respectively. +There are `CoProduct` and `Choice` specializations for type unions of up to 8 different types: `CoProduct2` through `CoProduct8`, and `Choice2` through `Choice8`, respectively. ### Either diff --git a/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice2.java b/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice2.java index 7bd16fa1e..f38f681cf 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice2.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice2.java @@ -4,7 +4,6 @@ import com.jnape.palatable.lambda.adt.coproduct.CoProduct2; import com.jnape.palatable.lambda.functor.Applicative; import com.jnape.palatable.lambda.functor.Bifunctor; -import com.jnape.palatable.lambda.functor.Functor; import com.jnape.palatable.lambda.monad.Monad; import com.jnape.palatable.lambda.traversable.Traversable; @@ -12,12 +11,11 @@ import java.util.function.Function; /** - * Canonical ADT representation of {@link CoProduct2} that is also a {@link Functor} and {@link Bifunctor}. Unlike - * {@link Either}, there is no concept of "success" or "failure", so the domain of reasonable function semantics is - * more limited. + * Canonical ADT representation of {@link CoProduct2}. Unlike {@link Either}, there is no concept of "success" or + * "failure", so the domain of reasonable function semantics is more limited. * - * @param a type parameter representing the first possible type of this choice - * @param a type parameter representing the second possible type of this choice + * @param the first possible type + * @param the second possible type * @see Either * @see Choice3 */ @@ -97,9 +95,9 @@ public Applicative, App> traverse( * Static factory method for wrapping a value of type A in a {@link Choice2}. * * @param a the value - * @param a type parameter representing the first possible type of this choice - * @param a type parameter representing the second possible type of this choice - * @return the wrapped value as a Choice2<A, B> + * @param the first possible type + * @param the second possible type + * @return the wrapped value as a {@link Choice2}<A, B> */ public static Choice2 a(A a) { return new _A<>(a); @@ -109,9 +107,9 @@ public static Choice2 a(A a) { * Static factory method for wrapping a value of type B in a {@link Choice2}. * * @param b the value - * @param a type parameter representing the first possible type of this choice - * @param a type parameter representing the second possible type of this choice - * @return the wrapped value as a Choice2<A, B> + * @param the first possible type + * @param the second possible type + * @return the wrapped value as a {@link Choice2}<A, B> */ public static Choice2 b(B b) { return new _B<>(b); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice3.java b/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice3.java index 8648ebc90..4ef49ac33 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice3.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice3.java @@ -4,7 +4,6 @@ import com.jnape.palatable.lambda.adt.coproduct.CoProduct3; import com.jnape.palatable.lambda.functor.Applicative; import com.jnape.palatable.lambda.functor.Bifunctor; -import com.jnape.palatable.lambda.functor.Functor; import com.jnape.palatable.lambda.monad.Monad; import com.jnape.palatable.lambda.traversable.Traversable; @@ -12,11 +11,11 @@ import java.util.function.Function; /** - * Canonical ADT representation of {@link CoProduct3} that is also a {@link Functor} and {@link Bifunctor}. + * Canonical ADT representation of {@link CoProduct3}. * - * @param a type parameter representing the first possible type of this choice - * @param a type parameter representing the second possible type of this choice - * @param a type parameter representing the third possible type of this choice + * @param the first possible type + * @param the second possible type + * @param the third possible type * @see Choice2 * @see Choice4 */ @@ -101,10 +100,10 @@ public Applicative, App> traverse( * Static factory method for wrapping a value of type A in a {@link Choice3}. * * @param a the value - * @param a type parameter representing the first possible type of this choice - * @param a type parameter representing the second possible type of this choice - * @param a type parameter representing the third possible type of this choice - * @return the wrapped value as a Choice3<A, B, C> + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @return the wrapped value as a {@link Choice3}<A, B, C> */ public static Choice3 a(A a) { return new _A<>(a); @@ -114,10 +113,10 @@ public static Choice3 a(A a) { * Static factory method for wrapping a value of type A in a {@link Choice3}. * * @param b the value - * @param a type parameter representing the first possible type of this choice - * @param a type parameter representing the second possible type of this choice - * @param a type parameter representing the third possible type of this choice - * @return the wrapped value as a Choice3<A, B, C> + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @return the wrapped value as a {@link Choice3}<A, B, C> */ public static Choice3 b(B b) { return new _B<>(b); @@ -127,10 +126,10 @@ public static Choice3 b(B b) { * Static factory method for wrapping a value of type A in a {@link Choice3}. * * @param c the value - * @param a type parameter representing the first possible type of this choice - * @param a type parameter representing the second possible type of this choice - * @param a type parameter representing the third possible type of this choice - * @return the wrapped value as a Choice3<A, B, C> + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @return the wrapped value as a {@link Choice3}<A, B, C> */ public static Choice3 c(C c) { return new _C<>(c); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice4.java b/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice4.java index 4ca44d6c7..dc98c7231 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice4.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice4.java @@ -4,7 +4,6 @@ import com.jnape.palatable.lambda.adt.coproduct.CoProduct4; import com.jnape.palatable.lambda.functor.Applicative; import com.jnape.palatable.lambda.functor.Bifunctor; -import com.jnape.palatable.lambda.functor.Functor; import com.jnape.palatable.lambda.monad.Monad; import com.jnape.palatable.lambda.traversable.Traversable; @@ -12,12 +11,12 @@ import java.util.function.Function; /** - * Canonical ADT representation of {@link CoProduct4} that is also a {@link Functor} and {@link Bifunctor}. + * Canonical ADT representation of {@link CoProduct4}. * - * @param a type parameter representing the first possible type of this choice - * @param a type parameter representing the second possible type of this choice - * @param a type parameter representing the third possible type of this choice - * @param a type parameter representing the fourth possible type of this choice + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type * @see Choice3 * @see Choice5 */ @@ -106,11 +105,11 @@ public Applicative, App> traver * Static factory method for wrapping a value of type A in a {@link Choice4}. * * @param a the value - * @param a type parameter representing the first possible type of this choice - * @param a type parameter representing the second possible type of this choice - * @param a type parameter representing the third possible type of this choice - * @param a type parameter representing the fourth possible type of this choice - * @return the wrapped value as a Choice4<A, B, C, D> + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @return the wrapped value as a {@link Choice4}<A, B, C, D> */ public static Choice4 a(A a) { return new _A<>(a); @@ -120,11 +119,11 @@ public static Choice4 a(A a) { * Static factory method for wrapping a value of type B in a {@link Choice4}. * * @param b the value - * @param a type parameter representing the first possible type of this choice - * @param a type parameter representing the second possible type of this choice - * @param a type parameter representing the third possible type of this choice - * @param a type parameter representing the fourth possible type of this choice - * @return the wrapped value as a Choice4<A, B, C, D> + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @return the wrapped value as a {@link Choice4}<A, B, C, D> */ public static Choice4 b(B b) { return new _B<>(b); @@ -134,11 +133,11 @@ public static Choice4 b(B b) { * Static factory method for wrapping a value of type C in a {@link Choice4}. * * @param c the value - * @param a type parameter representing the first possible type of this choice - * @param a type parameter representing the second possible type of this choice - * @param a type parameter representing the third possible type of this choice - * @param a type parameter representing the fourth possible type of this choice - * @return the wrapped value as a Choice4<A, B, C, D> + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @return the wrapped value as a {@link Choice4}<A, B, C, D> */ public static Choice4 c(C c) { return new _C<>(c); @@ -148,11 +147,11 @@ public static Choice4 c(C c) { * Static factory method for wrapping a value of type D in a {@link Choice4}. * * @param d the value - * @param a type parameter representing the first possible type of this choice - * @param a type parameter representing the second possible type of this choice - * @param a type parameter representing the third possible type of this choice - * @param a type parameter representing the fourth possible type of this choice - * @return the wrapped value as a Choice4<A, B, C, D> + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @return the wrapped value as a {@link Choice4}<A, B, C, D> */ public static Choice4 d(D d) { return new _D<>(d); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice5.java b/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice5.java index 21c03fffe..f3d609dd4 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice5.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice5.java @@ -4,7 +4,6 @@ import com.jnape.palatable.lambda.adt.coproduct.CoProduct5; import com.jnape.palatable.lambda.functor.Applicative; import com.jnape.palatable.lambda.functor.Bifunctor; -import com.jnape.palatable.lambda.functor.Functor; import com.jnape.palatable.lambda.monad.Monad; import com.jnape.palatable.lambda.traversable.Traversable; @@ -12,14 +11,15 @@ import java.util.function.Function; /** - * Canonical ADT representation of {@link CoProduct5} that is also a {@link Functor} and {@link Bifunctor}. + * Canonical ADT representation of {@link CoProduct5}. * - * @param a type parameter representing the first possible type of this choice - * @param a type parameter representing the second possible type of this choice - * @param a type parameter representing the third possible type of this choice - * @param a type parameter representing the fourth possible type of this choice - * @param a type parameter representing the fifth possible type of this choice + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type * @see Choice4 + * @see Choice6 */ public abstract class Choice5 implements CoProduct5>, @@ -30,6 +30,11 @@ public abstract class Choice5 implements private Choice5() { } + @Override + public Choice6 diverge() { + return match(Choice6::a, Choice6::b, Choice6::c, Choice6::d, Choice6::e); + } + @Override public Choice4 converge(Function> convergenceFn) { return match(Choice4::a, @@ -103,12 +108,12 @@ public Applicative, App> tra * Static factory method for wrapping a value of type A in a {@link Choice5}. * * @param a the value - * @param a type parameter representing the first possible type of this choice - * @param a type parameter representing the second possible type of this choice - * @param a type parameter representing the third possible type of this choice - * @param a type parameter representing the fourth possible type of this choice - * @param a type parameter representing the fifth possible type of this choice - * @return the wrapped value as a Choice5<A, B, C, D, E> + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @return the wrapped value as a {@link Choice5}<A, B, C, D, E> */ public static Choice5 a(A a) { return new _A<>(a); @@ -118,12 +123,12 @@ public static Choice5 a(A a) { * Static factory method for wrapping a value of type B in a {@link Choice5}. * * @param b the value - * @param a type parameter representing the first possible type of this choice - * @param a type parameter representing the second possible type of this choice - * @param a type parameter representing the third possible type of this choice - * @param a type parameter representing the fourth possible type of this choice - * @param a type parameter representing the fifth possible type of this choice - * @return the wrapped value as a Choice5<A, B, C, D, E> + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @return the wrapped value as a {@link Choice5}<A, B, C, D, E> */ public static Choice5 b(B b) { return new _B<>(b); @@ -133,12 +138,12 @@ public static Choice5 b(B b) { * Static factory method for wrapping a value of type C in a {@link Choice5}. * * @param c the value - * @param a type parameter representing the first possible type of this choice - * @param a type parameter representing the second possible type of this choice - * @param a type parameter representing the third possible type of this choice - * @param a type parameter representing the fourth possible type of this choice - * @param a type parameter representing the fifth possible type of this choice - * @return the wrapped value as a Choice5<A, B, C, D, E> + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @return the wrapped value as a {@link Choice5}<A, B, C, D, E> */ public static Choice5 c(C c) { return new _C<>(c); @@ -148,12 +153,12 @@ public static Choice5 c(C c) { * Static factory method for wrapping a value of type D in a {@link Choice5}. * * @param d the value - * @param a type parameter representing the first possible type of this choice - * @param a type parameter representing the second possible type of this choice - * @param a type parameter representing the third possible type of this choice - * @param a type parameter representing the fourth possible type of this choice - * @param a type parameter representing the fifth possible type of this choice - * @return the wrapped value as a Choice5<A, B, C, D, E> + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @return the wrapped value as a {@link Choice5}<A, B, C, D, E> */ public static Choice5 d(D d) { return new _D<>(d); @@ -163,12 +168,12 @@ public static Choice5 d(D d) { * Static factory method for wrapping a value of type E in a {@link Choice5}. * * @param e the value - * @param a type parameter representing the first possible type of this choice - * @param a type parameter representing the second possible type of this choice - * @param a type parameter representing the third possible type of this choice - * @param a type parameter representing the fourth possible type of this choice - * @param a type parameter representing the fifth possible type of this choice - * @return the wrapped value as a Choice5<A, B, C, D, E> + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @return the wrapped value as a {@link Choice5}<A, B, C, D, E> */ public static Choice5 e(E e) { return new _E<>(e); diff --git a/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice6.java b/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice6.java new file mode 100644 index 000000000..b351db250 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice6.java @@ -0,0 +1,399 @@ +package com.jnape.palatable.lambda.adt.choice; + +import com.jnape.palatable.lambda.adt.coproduct.CoProduct5; +import com.jnape.palatable.lambda.adt.coproduct.CoProduct6; +import com.jnape.palatable.lambda.functor.Applicative; +import com.jnape.palatable.lambda.functor.Bifunctor; +import com.jnape.palatable.lambda.monad.Monad; +import com.jnape.palatable.lambda.traversable.Traversable; + +import java.util.Objects; +import java.util.function.Function; + +/** + * Canonical ADT representation of {@link CoProduct6}. + * + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @see Choice5 + * @see Choice7 + */ +public abstract class Choice6 implements + CoProduct6>, + Monad>, + Bifunctor>, + Traversable> { + + private Choice6() { + } + + @Override + public Choice7 diverge() { + return match(Choice7::a, Choice7::b, Choice7::c, Choice7::d, Choice7::e, Choice7::f); + } + + @Override + public Choice5 converge(Function> convergenceFn) { + return match(Choice5::a, + Choice5::b, + Choice5::c, + Choice5::d, + Choice5::e, + convergenceFn.andThen(cp5 -> cp5.match(Choice5::a, Choice5::b, Choice5::c, Choice5::d, Choice5::e))); + } + + @Override + public Choice6 fmap(Function fn) { + return Monad.super.fmap(fn).coerce(); + } + + @Override + @SuppressWarnings("unchecked") + public Choice6 biMapL(Function fn) { + return (Choice6) Bifunctor.super.biMapL(fn); + } + + @Override + @SuppressWarnings("unchecked") + public Choice6 biMapR(Function fn) { + return (Choice6) Bifunctor.super.biMapR(fn); + } + + @Override + public Choice6 biMap(Function lFn, + Function rFn) { + return match(Choice6::a, Choice6::b, Choice6::c, Choice6::d, e -> e(lFn.apply(e)), f -> f(rFn.apply(f))); + } + + @Override + public Choice6 pure(G g) { + return f(g); + } + + @Override + public Choice6 zip( + Applicative, Choice6> appFn) { + return appFn.>>coerce() + .match(Choice6::a, Choice6::b, Choice6::c, Choice6::d, Choice6::e, this::biMapR); + } + + @Override + public Choice6 discardL(Applicative> appB) { + return Monad.super.discardL(appB).coerce(); + } + + @Override + public Choice6 discardR(Applicative> appB) { + return Monad.super.discardR(appB).coerce(); + } + + @Override + public Choice6 flatMap( + Function>> fn) { + return match(Choice6::a, Choice6::b, Choice6::c, Choice6::d, Choice6::e, f -> fn.apply(f).coerce()); + } + + @Override + public Applicative, App> traverse( + Function> fn, + Function>, ? extends Applicative>, App>> pure) { + return match(a -> pure.apply(a(a)).fmap(x -> (Choice6) x), + b -> pure.apply(b(b)).fmap(x -> (Choice6) x), + c -> pure.apply(c(c)).fmap(x -> (Choice6) x), + d -> pure.apply(d(d)).fmap(x -> (Choice6) x), + e -> pure.apply(e(e)).fmap(x -> (Choice6) x), + f -> fn.apply(f).fmap(Choice6::f)); + } + + /** + * Static factory method for wrapping a value of type A in a {@link Choice6}. + * + * @param a the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @return the wrapped value as a {@link Choice6}<A, B, C, D, E, F> + */ + public static Choice6 a(A a) { + return new _A<>(a); + } + + /** + * Static factory method for wrapping a value of type B in a {@link Choice6}. + * + * @param b the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @return the wrapped value as a {@link Choice6}<A, B, C, D, E, F> + */ + public static Choice6 b(B b) { + return new _B<>(b); + } + + /** + * Static factory method for wrapping a value of type C in a {@link Choice6}. + * + * @param c the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @return the wrapped value as a {@link Choice6}<A, B, C, D, E, F> + */ + public static Choice6 c(C c) { + return new _C<>(c); + } + + /** + * Static factory method for wrapping a value of type D in a {@link Choice6}. + * + * @param d the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @return the wrapped value as a {@link Choice6}<A, B, C, D, E, F> + */ + public static Choice6 d(D d) { + return new _D<>(d); + } + + /** + * Static factory method for wrapping a value of type E in a {@link Choice6}. + * + * @param e the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @return the wrapped value as a {@link Choice6}<A, B, C, D, E, F> + */ + public static Choice6 e(E e) { + return new _E<>(e); + } + + /** + * Static factory method for wrapping a value of type F in a {@link Choice6}. + * + * @param f the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @return the wrapped value as a {@link Choice6}<A, B, C, D, E, F> + */ + public static Choice6 f(F f) { + return new _F<>(f); + } + + private static final class _A extends Choice6 { + + private final A a; + + private _A(A a) { + this.a = a; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn) { + return aFn.apply(a); + } + + @Override + public boolean equals(Object other) { + return other instanceof _A + && Objects.equals(a, ((_A) other).a); + } + + @Override + public int hashCode() { + return Objects.hash(a); + } + + @Override + public String toString() { + return "Choice6{a=" + a + '}'; + } + } + + private static final class _B extends Choice6 { + + private final B b; + + private _B(B b) { + this.b = b; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn) { + return bFn.apply(b); + } + + @Override + public boolean equals(Object other) { + return other instanceof _B + && Objects.equals(b, ((_B) other).b); + } + + @Override + public int hashCode() { + return Objects.hash(b); + } + + @Override + public String toString() { + return "Choice6{b=" + b + '}'; + } + } + + private static final class _C extends Choice6 { + + private final C c; + + private _C(C c) { + this.c = c; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn) { + return cFn.apply(c); + } + + @Override + public boolean equals(Object other) { + return other instanceof _C + && Objects.equals(c, ((_C) other).c); + } + + @Override + public int hashCode() { + return Objects.hash(c); + } + + @Override + public String toString() { + return "Choice6{c=" + c + '}'; + } + } + + private static final class _D extends Choice6 { + + private final D d; + + private _D(D d) { + this.d = d; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn) { + return dFn.apply(d); + } + + @Override + public boolean equals(Object other) { + return other instanceof _D + && Objects.equals(d, ((_D) other).d); + } + + @Override + public int hashCode() { + return Objects.hash(d); + } + + @Override + public String toString() { + return "Choice6{d=" + d + '}'; + } + } + + private static final class _E extends Choice6 { + + private final E e; + + private _E(E e) { + this.e = e; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn) { + return eFn.apply(e); + } + + @Override + public boolean equals(Object other) { + return other instanceof _E + && Objects.equals(e, ((_E) other).e); + } + + @Override + public int hashCode() { + return Objects.hash(e); + } + + @Override + public String toString() { + return "Choice6{e=" + e + '}'; + } + } + + private static final class _F extends Choice6 { + + private final F f; + + private _F(F f) { + this.f = f; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn) { + return fFn.apply(f); + } + + @Override + public boolean equals(Object other) { + return other instanceof _F + && Objects.equals(f, ((_F) other).f); + } + + @Override + public int hashCode() { + return Objects.hash(f); + } + + @Override + public String toString() { + return "Choice6{f=" + f + '}'; + } + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice7.java b/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice7.java new file mode 100644 index 000000000..920a707db --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice7.java @@ -0,0 +1,465 @@ +package com.jnape.palatable.lambda.adt.choice; + +import com.jnape.palatable.lambda.adt.coproduct.CoProduct6; +import com.jnape.palatable.lambda.adt.coproduct.CoProduct7; +import com.jnape.palatable.lambda.functor.Applicative; +import com.jnape.palatable.lambda.functor.Bifunctor; +import com.jnape.palatable.lambda.monad.Monad; +import com.jnape.palatable.lambda.traversable.Traversable; + +import java.util.Objects; +import java.util.function.Function; + +/** + * Canonical ADT representation of {@link CoProduct7}. + * + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @see Choice6 + * @see Choice8 + */ +public abstract class Choice7 implements + CoProduct7>, + Monad>, + Bifunctor>, + Traversable> { + + private Choice7() { + } + + @Override + public Choice8 diverge() { + return match(Choice8::a, Choice8::b, Choice8::c, Choice8::d, Choice8::e, Choice8::f, Choice8::g); + } + + @Override + public Choice6 converge( + Function> convergenceFn) { + return match(Choice6::a, + Choice6::b, + Choice6::c, + Choice6::d, + Choice6::e, + Choice6::f, + convergenceFn.andThen(cp6 -> cp6.match(Choice6::a, Choice6::b, Choice6::c, Choice6::d, Choice6::e, Choice6::f))); + } + + @Override + public Choice7 fmap(Function fn) { + return Monad.super.fmap(fn).coerce(); + } + + @Override + @SuppressWarnings("unchecked") + public Choice7 biMapL(Function fn) { + return (Choice7) Bifunctor.super.biMapL(fn); + } + + @Override + @SuppressWarnings("unchecked") + public Choice7 biMapR(Function fn) { + return (Choice7) Bifunctor.super.biMapR(fn); + } + + @Override + public Choice7 biMap(Function lFn, + Function rFn) { + return match(Choice7::a, Choice7::b, Choice7::c, Choice7::d, Choice7::e, f -> f(lFn.apply(f)), g -> g(rFn.apply(g))); + } + + @Override + public Choice7 pure(H h) { + return g(h); + } + + @Override + public Choice7 zip( + Applicative, Choice7> appFn) { + return appFn.>>coerce() + .match(Choice7::a, Choice7::b, Choice7::c, Choice7::d, Choice7::e, Choice7::f, this::biMapR); + } + + @Override + public Choice7 discardL(Applicative> appB) { + return Monad.super.discardL(appB).coerce(); + } + + @Override + public Choice7 discardR(Applicative> appB) { + return Monad.super.discardR(appB).coerce(); + } + + @Override + public Choice7 flatMap( + Function>> fn) { + return match(Choice7::a, Choice7::b, Choice7::c, Choice7::d, Choice7::e, Choice7::f, g -> fn.apply(g).coerce()); + } + + @Override + public Applicative, App> traverse( + Function> fn, + Function>, ? extends Applicative>, App>> pure) { + return match(a -> pure.apply(a(a)).fmap(x -> (Choice7) x), + b -> pure.apply(b(b)).fmap(x -> (Choice7) x), + c -> pure.apply(c(c)).fmap(x -> (Choice7) x), + d -> pure.apply(d(d)).fmap(x -> (Choice7) x), + e -> pure.apply(e(e)).fmap(x -> (Choice7) x), + f -> pure.apply(f(f)).fmap(x -> (Choice7) x), + g -> fn.apply(g).fmap(Choice7::g)); + } + + /** + * Static factory method for wrapping a value of type A in a {@link Choice7}. + * + * @param a the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @return the wrapped value as a {@link Choice7}<A, B, C, D, E, F, G> + */ + public static Choice7 a(A a) { + return new _A<>(a); + } + + /** + * Static factory method for wrapping a value of type B in a {@link Choice7}. + * + * @param b the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @return the wrapped value as a {@link Choice7}<A, B, C, D, E, F, G> + */ + public static Choice7 b(B b) { + return new _B<>(b); + } + + /** + * Static factory method for wrapping a value of type C in a {@link Choice7}. + * + * @param c the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @return the wrapped value as a {@link Choice7}<A, B, C, D, E, F, G> + */ + public static Choice7 c(C c) { + return new _C<>(c); + } + + /** + * Static factory method for wrapping a value of type D in a {@link Choice7}. + * + * @param d the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @return the wrapped value as a {@link Choice7}<A, B, C, D, E, F, G> + */ + public static Choice7 d(D d) { + return new _D<>(d); + } + + /** + * Static factory method for wrapping a value of type E in a {@link Choice7}. + * + * @param e the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @return the wrapped value as a {@link Choice7}<A, B, C, D, E, F, G> + */ + public static Choice7 e(E e) { + return new _E<>(e); + } + + /** + * Static factory method for wrapping a value of type F in a {@link Choice7}. + * + * @param f the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @return the wrapped value as a {@link Choice7}<A, B, C, D, E, F, G> + */ + public static Choice7 f(F f) { + return new _F<>(f); + } + + /** + * Static factory method for wrapping a value of type G in a {@link Choice7}. + * + * @param g the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @return the wrapped value as a {@link Choice7}<A, B, C, D, E, F, G> + */ + public static Choice7 g(G g) { + return new _G<>(g); + } + + private static final class _A extends Choice7 { + + private final A a; + + private _A(A a) { + this.a = a; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn) { + return aFn.apply(a); + } + + @Override + public boolean equals(Object other) { + return other instanceof _A + && Objects.equals(a, ((_A) other).a); + } + + @Override + public int hashCode() { + return Objects.hash(a); + } + + @Override + public String toString() { + return "Choice7{a=" + a + '}'; + } + } + + private static final class _B extends Choice7 { + + private final B b; + + private _B(B b) { + this.b = b; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn) { + return bFn.apply(b); + } + + @Override + public boolean equals(Object other) { + return other instanceof _B + && Objects.equals(b, ((_B) other).b); + } + + @Override + public int hashCode() { + return Objects.hash(b); + } + + @Override + public String toString() { + return "Choice7{b=" + b + '}'; + } + } + + private static final class _C extends Choice7 { + + private final C c; + + private _C(C c) { + this.c = c; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn) { + return cFn.apply(c); + } + + @Override + public boolean equals(Object other) { + return other instanceof _C + && Objects.equals(c, ((_C) other).c); + } + + @Override + public int hashCode() { + return Objects.hash(c); + } + + @Override + public String toString() { + return "Choice7{c=" + c + '}'; + } + } + + private static final class _D extends Choice7 { + + private final D d; + + private _D(D d) { + this.d = d; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn) { + return dFn.apply(d); + } + + @Override + public boolean equals(Object other) { + return other instanceof _D + && Objects.equals(d, ((_D) other).d); + } + + @Override + public int hashCode() { + return Objects.hash(d); + } + + @Override + public String toString() { + return "Choice7{d=" + d + '}'; + } + } + + private static final class _E extends Choice7 { + + private final E e; + + private _E(E e) { + this.e = e; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn) { + return eFn.apply(e); + } + + @Override + public boolean equals(Object other) { + return other instanceof _E + && Objects.equals(e, ((_E) other).e); + } + + @Override + public int hashCode() { + return Objects.hash(e); + } + + @Override + public String toString() { + return "Choice7{e=" + e + '}'; + } + } + + private static final class _F extends Choice7 { + + private final F f; + + private _F(F f) { + this.f = f; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn) { + return fFn.apply(f); + } + + @Override + public boolean equals(Object other) { + return other instanceof _F + && Objects.equals(f, ((_F) other).f); + } + + @Override + public int hashCode() { + return Objects.hash(f); + } + + @Override + public String toString() { + return "Choice7{f=" + f + '}'; + } + } + + private static final class _G extends Choice7 { + + private final G g; + + private _G(G g) { + this.g = g; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn) { + return gFn.apply(g); + } + + @Override + public boolean equals(Object other) { + return other instanceof _G + && Objects.equals(g, ((_G) other).g); + } + + @Override + public int hashCode() { + return Objects.hash(g); + } + + @Override + public String toString() { + return "Choice7{g=" + g + '}'; + } + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice8.java b/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice8.java new file mode 100644 index 000000000..b9d070c39 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice8.java @@ -0,0 +1,521 @@ +package com.jnape.palatable.lambda.adt.choice; + +import com.jnape.palatable.lambda.adt.coproduct.CoProduct7; +import com.jnape.palatable.lambda.adt.coproduct.CoProduct8; +import com.jnape.palatable.lambda.functor.Applicative; +import com.jnape.palatable.lambda.functor.Bifunctor; +import com.jnape.palatable.lambda.monad.Monad; +import com.jnape.palatable.lambda.traversable.Traversable; + +import java.util.Objects; +import java.util.function.Function; + +/** + * Canonical ADT representation of {@link CoProduct8}. + * + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @param the eighth possible type + * @see Choice7 + */ +public abstract class Choice8 implements + CoProduct8>, + Monad>, + Bifunctor>, + Traversable> { + + private Choice8() { + } + + @Override + public Choice7 converge( + Function> convergenceFn) { + return match(Choice7::a, + Choice7::b, + Choice7::c, + Choice7::d, + Choice7::e, + Choice7::f, + Choice7::g, + convergenceFn.andThen(cp7 -> cp7.match(Choice7::a, Choice7::b, Choice7::c, Choice7::d, Choice7::e, Choice7::f, Choice7::g))); + } + + @Override + public Choice8 fmap(Function fn) { + return Monad.super.fmap(fn).coerce(); + } + + @Override + @SuppressWarnings("unchecked") + public Choice8 biMapL(Function fn) { + return (Choice8) Bifunctor.super.biMapL(fn); + } + + @Override + @SuppressWarnings("unchecked") + public Choice8 biMapR(Function fn) { + return (Choice8) Bifunctor.super.biMapR(fn); + } + + @Override + public Choice8 biMap(Function lFn, + Function rFn) { + return match(Choice8::a, Choice8::b, Choice8::c, Choice8::d, Choice8::e, Choice8::f, g -> g(lFn.apply(g)), h -> h(rFn.apply(h))); + } + + @Override + public Choice8 pure(I i) { + return h(i); + } + + @Override + public Choice8 zip( + Applicative, Choice8> appFn) { + return appFn.>>coerce() + .match(Choice8::a, Choice8::b, Choice8::c, Choice8::d, Choice8::e, Choice8::f, Choice8::g, this::biMapR); + } + + @Override + public Choice8 discardL(Applicative> appB) { + return Monad.super.discardL(appB).coerce(); + } + + @Override + public Choice8 discardR(Applicative> appB) { + return Monad.super.discardR(appB).coerce(); + } + + @Override + public Choice8 flatMap( + Function>> fn) { + return match(Choice8::a, Choice8::b, Choice8::c, Choice8::d, Choice8::e, Choice8::f, Choice8::g, h -> fn.apply(h).coerce()); + } + + @Override + public Applicative, App> traverse( + Function> fn, + Function>, ? extends Applicative>, App>> pure) { + return match(a -> pure.apply(a(a)).fmap(x -> (Choice8) x), + b -> pure.apply(b(b)).fmap(x -> (Choice8) x), + c -> pure.apply(c(c)).fmap(x -> (Choice8) x), + d -> pure.apply(d(d)).fmap(x -> (Choice8) x), + e -> pure.apply(e(e)).fmap(x -> (Choice8) x), + f -> pure.apply(f(f)).fmap(x -> (Choice8) x), + g -> pure.apply(g(g)).fmap(x -> (Choice8) x), + h -> fn.apply(h).fmap(Choice8::h)); + } + + /** + * Static factory method for wrapping a value of type A in a {@link Choice8}. + * + * @param a the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @param the eighth possible type + * @return the wrapped value as a {@link Choice8}<A, B, C, D, E, F, G, H> + */ + public static Choice8 a(A a) { + return new _A<>(a); + } + + /** + * Static factory method for wrapping a value of type B in a {@link Choice8}. + * + * @param b the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @param the eighth possible type + * @return the wrapped value as a {@link Choice8}<A, B, C, D, E, F, G, H> + */ + public static Choice8 b(B b) { + return new _B<>(b); + } + + /** + * Static factory method for wrapping a value of type C in a {@link Choice8}. + * + * @param c the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @param the eighth possible type + * @return the wrapped value as a {@link Choice8}<A, B, C, D, E, F, G, H> + */ + public static Choice8 c(C c) { + return new _C<>(c); + } + + /** + * Static factory method for wrapping a value of type D in a {@link Choice8}. + * + * @param d the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @param the eighth possible type + * @return the wrapped value as a {@link Choice8}<A, B, C, D, E, F, G, H> + */ + public static Choice8 d(D d) { + return new _D<>(d); + } + + /** + * Static factory method for wrapping a value of type E in a {@link Choice8}. + * + * @param e the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @param the eighth possible type + * @return the wrapped value as a {@link Choice8}<A, B, C, D, E, F, G, H> + */ + public static Choice8 e(E e) { + return new _E<>(e); + } + + /** + * Static factory method for wrapping a value of type F in a {@link Choice8}. + * + * @param f the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @param the eighth possible type + * @return the wrapped value as a {@link Choice8}<A, B, C, D, E, F, G, H> + */ + public static Choice8 f(F f) { + return new _F<>(f); + } + + /** + * Static factory method for wrapping a value of type G in a {@link Choice8}. + * + * @param g the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @param the eighth possible type + * @return the wrapped value as a {@link Choice8}<A, B, C, D, E, F, G, H> + */ + public static Choice8 g(G g) { + return new _G<>(g); + } + + /** + * Static factory method for wrapping a value of type H in a {@link Choice8}. + * + * @param h the value + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @param the eighth possible type + * @return the wrapped value as a {@link Choice8}<A, B, C, D, E, F, G, H> + */ + public static Choice8 h(H h) { + return new _H<>(h); + } + + + private static final class _A extends Choice8 { + + private final A a; + + private _A(A a) { + this.a = a; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn, Function hFn) { + return aFn.apply(a); + } + + @Override + public boolean equals(Object other) { + return other instanceof _A + && Objects.equals(a, ((_A) other).a); + } + + @Override + public int hashCode() { + return Objects.hash(a); + } + + @Override + public String toString() { + return "Choice8{a=" + a + '}'; + } + } + + private static final class _B extends Choice8 { + + private final B b; + + private _B(B b) { + this.b = b; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn, Function hFn) { + return bFn.apply(b); + } + + @Override + public boolean equals(Object other) { + return other instanceof _B + && Objects.equals(b, ((_B) other).b); + } + + @Override + public int hashCode() { + return Objects.hash(b); + } + + @Override + public String toString() { + return "Choice8{b=" + b + '}'; + } + } + + private static final class _C extends Choice8 { + + private final C c; + + private _C(C c) { + this.c = c; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn, Function hFn) { + return cFn.apply(c); + } + + @Override + public boolean equals(Object other) { + return other instanceof _C + && Objects.equals(c, ((_C) other).c); + } + + @Override + public int hashCode() { + return Objects.hash(c); + } + + @Override + public String toString() { + return "Choice8{c=" + c + '}'; + } + } + + private static final class _D extends Choice8 { + + private final D d; + + private _D(D d) { + this.d = d; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn, Function hFn) { + return dFn.apply(d); + } + + @Override + public boolean equals(Object other) { + return other instanceof _D + && Objects.equals(d, ((_D) other).d); + } + + @Override + public int hashCode() { + return Objects.hash(d); + } + + @Override + public String toString() { + return "Choice8{d=" + d + '}'; + } + } + + private static final class _E extends Choice8 { + + private final E e; + + private _E(E e) { + this.e = e; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn, Function hFn) { + return eFn.apply(e); + } + + @Override + public boolean equals(Object other) { + return other instanceof _E + && Objects.equals(e, ((_E) other).e); + } + + @Override + public int hashCode() { + return Objects.hash(e); + } + + @Override + public String toString() { + return "Choice8{e=" + e + '}'; + } + } + + private static final class _F extends Choice8 { + + private final F f; + + private _F(F f) { + this.f = f; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn, Function hFn) { + return fFn.apply(f); + } + + @Override + public boolean equals(Object other) { + return other instanceof _F + && Objects.equals(f, ((_F) other).f); + } + + @Override + public int hashCode() { + return Objects.hash(f); + } + + @Override + public String toString() { + return "Choice8{f=" + f + '}'; + } + } + + private static final class _G extends Choice8 { + + private final G g; + + private _G(G g) { + this.g = g; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn, Function hFn) { + return gFn.apply(g); + } + + @Override + public boolean equals(Object other) { + return other instanceof _G + && Objects.equals(g, ((_G) other).g); + } + + @Override + public int hashCode() { + return Objects.hash(g); + } + + @Override + public String toString() { + return "Choice8{g=" + g + '}'; + } + } + + private static final class _H extends Choice8 { + + private final H h; + + private _H(H h) { + this.h = h; + } + + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn, Function hFn) { + return hFn.apply(h); + } + + @Override + public boolean equals(Object other) { + return other instanceof _H + && Objects.equals(h, ((_H) other).h); + } + + @Override + public int hashCode() { + return Objects.hash(h); + } + + @Override + public String toString() { + return "Choice8{h=" + h + '}'; + } + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2.java b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2.java index 650c7f006..5aadd08a0 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2.java @@ -15,14 +15,13 @@ import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly; /** - * A generalization of the coproduct of two types A and B. Coproducts represent the disjoint - * union of two or more distinct types, and provides an interface for specifying morphisms from those types to a common - * result type. + * A generalization of the coproduct of two types. Coproducts represent the disjoint union of two or more distinct + * types, and provides an interface for specifying morphisms from those types to a common result type. *

* Learn more about Coproducts. * - * @param a type parameter representing the first possible type of this coproduct - * @param a type parameter representing the second possible type of this coproduct + * @param the first possible type + * @param the second possible type * @see Choice2 * @see Either */ @@ -58,7 +57,7 @@ public interface CoProduct2> { * single magnitude difference. * * @param the additional possible type of this coproduct - * @return a coproduct of the initial types plus the new type + * @return a {@link CoProduct3}<A, B, C> */ default CoProduct3> diverge() { return new CoProduct3>() { diff --git a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3.java b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3.java index d3c27bc70..6937d4735 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3.java @@ -13,11 +13,11 @@ import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly; /** - * A generalization of the coproduct of three types A, B, and C. + * A generalization of the coproduct of three types. * - * @param a type parameter representing the first possible type of this coproduct - * @param a type parameter representing the second possible type of this coproduct - * @param a type parameter representing the third possible type of this coproduct + * @param the first possible type + * @param the second possible type + * @param the third possible type * @see CoProduct2 */ @FunctionalInterface @@ -30,7 +30,7 @@ public interface CoProduct3> { * @param bFn morphism B -> R * @param cFn morphism C -> R * @param result type - * @return the result of applying the appropriate morphism from whichever type is represented by this coproduct to R + * @return the result of applying the appropriate morphism to this coproduct's unwrapped value * @see CoProduct2#match(Function, Function) */ R match(Function aFn, Function bFn, @@ -40,7 +40,7 @@ R match(Function aFn, Function the additional possible type of this coproduct - * @return a Coproduct4<A, B, C, D> + * @return a {@link CoProduct4}<A, B, C, D> * @see CoProduct2#diverge() */ default CoProduct4> diverge() { @@ -64,7 +64,7 @@ public R match(Function aFn, Function * * @param convergenceFn function from last possible type to earlier type - * @return a coproduct of the initial types without the terminal type + * @return a {@link CoProduct2}<A, B> */ default CoProduct2> converge( Function> convergenceFn) { diff --git a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4.java b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4.java index 4d8373b66..96939fba9 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4.java @@ -13,12 +13,12 @@ import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly; /** - * A generalization of the coproduct of four types A, B, C, and D. + * A generalization of the coproduct of four types. * - * @param a type parameter representing the first possible type of this coproduct - * @param a type parameter representing the second possible type of this coproduct - * @param a type parameter representing the third possible type of this coproduct - * @param a type parameter representing the fourth possible type of this coproduct + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type * @see CoProduct2 */ @FunctionalInterface @@ -63,7 +63,7 @@ public R match(Function aFn, Function> converge( diff --git a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5.java b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5.java index cd91efb39..ffaf5ef1b 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5.java @@ -13,14 +13,13 @@ import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly; /** - * A generalization of the coproduct of five types A, B, C, D, and - * E. + * A generalization of the coproduct of five types. * - * @param a type parameter representing the first possible type of this coproduct - * @param a type parameter representing the second possible type of this coproduct - * @param a type parameter representing the third possible type of this coproduct - * @param a type parameter representing the fourth possible type of this coproduct - * @param a type parameter representing the fifth possible type of this coproduct + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type * @see CoProduct2 */ @FunctionalInterface @@ -44,12 +43,30 @@ R match(Function aFn, Function dFn, Function eFn); + /** + * Diverge this coproduct by introducing another possible type that it could represent. + * + * @param the additional possible type of this coproduct + * @return a Coproduct6<A, B, C, D, E, F> + * @see CoProduct2#diverge() + */ + default CoProduct6> diverge() { + return new CoProduct6>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn) { + return CoProduct5.this.match(aFn, bFn, cFn, dFn, eFn); + } + }; + } + /** * Converge this coproduct down to a lower order coproduct by mapping the last possible type into an earlier * possible type. * * @param convergenceFn morphism E -> {@link CoProduct4}<A, B, C, D> - * @return a CoProduct4<A, B, C, D> + * @return a {@link CoProduct4}<A, B, C, D> */ default CoProduct4> converge( Function> convergenceFn) { diff --git a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct6.java b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct6.java new file mode 100644 index 000000000..947036ce1 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct6.java @@ -0,0 +1,179 @@ +package com.jnape.palatable.lambda.adt.coproduct; + +import com.jnape.palatable.lambda.adt.Maybe; +import com.jnape.palatable.lambda.adt.choice.Choice5; +import com.jnape.palatable.lambda.adt.hlist.Tuple6; +import com.jnape.palatable.lambda.functions.Fn1; + +import java.util.function.Function; + +import static com.jnape.palatable.lambda.adt.Maybe.just; +import static com.jnape.palatable.lambda.adt.Maybe.nothing; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.Fn1.fn1; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly; + +/** + * A generalization of the coproduct of six types. + * + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @see CoProduct2 + */ +@FunctionalInterface +public interface CoProduct6> { + + /** + * Type-safe convergence requiring a match against all potential types. + * + * @param aFn morphism A -> R + * @param bFn morphism B -> R + * @param cFn morphism C -> R + * @param dFn morphism D -> R + * @param eFn morphism E -> R + * @param fFn morphism F -> R + * @param result type + * @return the result of applying the appropriate morphism from whichever type is represented by this coproduct to R + * @see CoProduct2#match(Function, Function) + */ + R match(Function aFn, + Function bFn, + Function cFn, + Function dFn, + Function eFn, + Function fFn); + + /** + * Diverge this coproduct by introducing another possible type that it could represent. + * + * @param the additional possible type of this coproduct + * @return a Coproduct7<A, B, C, D, E, F, G> + * @see CoProduct2#diverge() + */ + default CoProduct7> diverge() { + return new CoProduct7>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn) { + return CoProduct6.this.match(aFn, bFn, cFn, dFn, eFn, fFn); + } + }; + } + + /** + * Converge this coproduct down to a lower order coproduct by mapping the last possible type into an earlier + * possible type. + * + * @param convergenceFn morphism F -> {@link CoProduct5}<A, B, C, D, E> + * @return a {@link CoProduct5}<A, B, C, D, E> + */ + default CoProduct5> converge( + Function> convergenceFn) { + return match(Choice5::a, Choice5::b, Choice5::c, Choice5::d, Choice5::e, convergenceFn::apply); + } + + /** + * Project this coproduct onto a tuple. + * + * @return a tuple of the coproduct projection + * @see CoProduct2#project() + */ + default Tuple6, Maybe, Maybe, Maybe, Maybe, Maybe> project() { + return match(a -> tuple(just(a), nothing(), nothing(), nothing(), nothing(), nothing()), + b -> tuple(nothing(), just(b), nothing(), nothing(), nothing(), nothing()), + c -> tuple(nothing(), nothing(), just(c), nothing(), nothing(), nothing()), + d -> tuple(nothing(), nothing(), nothing(), just(d), nothing(), nothing()), + e -> tuple(nothing(), nothing(), nothing(), nothing(), just(e), nothing()), + f -> tuple(nothing(), nothing(), nothing(), nothing(), nothing(), just(f))); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the first slot value. + * + * @return an optional value representing the projection of the "a" type index + */ + default Maybe projectA() { + return project()._1(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the second slot value. + * + * @return an optional value representing the projection of the "b" type index + */ + default Maybe projectB() { + return project()._2(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the third slot value. + * + * @return an optional value representing the projection of the "c" type index + */ + default Maybe projectC() { + return project()._3(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the fourth slot value. + * + * @return an optional value representing the projection of the "d" type index + */ + default Maybe projectD() { + return project()._4(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the fifth slot value. + * + * @return an optional value representing the projection of the "e" type index + */ + default Maybe projectE() { + return project()._5(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the sixth slot value. + * + * @return an optional value representing the projection of the "f" type index + */ + default Maybe projectF() { + return project()._6(); + } + + /** + * Embed this coproduct inside another value; that is, given morphisms from this coproduct to R, apply + * the appropriate morphism to this coproduct as a whole. Like {@link CoProduct6#match}, but without unwrapping the + * value. + * + * @param aFn morphism A v B v C v D v E v F -> R, applied in the A case + * @param bFn morphism A v B v C v D v E v F -> R, applied in the B case + * @param cFn morphism A v B v C v D v E v F -> R, applied in the C case + * @param dFn morphism A v B v C v D v E v F -> R, applied in the D case + * @param eFn morphism A v B v C v D v E v F -> R, applied in the E case + * @param fFn morphism A v B v C v D v E v F -> R, applied in the F case + * @param result type + * @return the result of applying the appropriate morphism to this coproduct + */ + @SuppressWarnings("unchecked") + default R embed(Function aFn, + Function bFn, + Function cFn, + Function dFn, + Function eFn, + Function fFn) { + return this.>match(constantly(fn1(aFn)), + constantly(fn1(bFn)), + constantly(fn1(cFn)), + constantly(fn1(dFn)), + constantly(fn1(eFn)), + constantly(fn1(fFn))) + .apply((CP6) this); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct7.java b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct7.java new file mode 100644 index 000000000..3b57fce9b --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct7.java @@ -0,0 +1,195 @@ +package com.jnape.palatable.lambda.adt.coproduct; + +import com.jnape.palatable.lambda.adt.Maybe; +import com.jnape.palatable.lambda.adt.choice.Choice6; +import com.jnape.palatable.lambda.adt.hlist.Tuple7; +import com.jnape.palatable.lambda.functions.Fn1; + +import java.util.function.Function; + +import static com.jnape.palatable.lambda.adt.Maybe.just; +import static com.jnape.palatable.lambda.adt.Maybe.nothing; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.Fn1.fn1; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly; + +/** + * A generalization of the coproduct of seven types. + * + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @see CoProduct2 + */ +@FunctionalInterface +public interface CoProduct7> { + + /** + * Type-safe convergence requiring a match against all potential types. + * + * @param aFn morphism A -> R + * @param bFn morphism B -> R + * @param cFn morphism C -> R + * @param dFn morphism D -> R + * @param eFn morphism E -> R + * @param fFn morphism F -> R + * @param gFn morphism G -> R + * @param result type + * @return the result of applying the appropriate morphism from whichever type is represented by this coproduct to R + * @see CoProduct2#match(Function, Function) + */ + R match(Function aFn, + Function bFn, + Function cFn, + Function dFn, + Function eFn, + Function fFn, + Function gFn); + + /** + * Diverge this coproduct by introducing another possible type that it could represent. + * + * @param the additional possible type of this coproduct + * @return a Coproduct8<A, B, C, D, E, F, G, H> + * @see CoProduct2#diverge() + */ + default CoProduct8> diverge() { + return new CoProduct8>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn, Function hFn) { + return CoProduct7.this.match(aFn, bFn, cFn, dFn, eFn, fFn, gFn); + } + }; + } + + /** + * Converge this coproduct down to a lower order coproduct by mapping the last possible type into an earlier + * possible type. + * + * @param convergenceFn morphism G -> {@link CoProduct6}<A, B, C, D, E, F> + * @return a {@link CoProduct6}<A, B, C, D, E, F> + */ + default CoProduct6> converge( + Function> convergenceFn) { + return match(Choice6::a, Choice6::b, Choice6::c, Choice6::d, Choice6::e, Choice6::f, convergenceFn::apply); + } + + /** + * Project this coproduct onto a tuple. + * + * @return a tuple of the coproduct projection + * @see CoProduct2#project() + */ + default Tuple7, Maybe, Maybe, Maybe, Maybe, Maybe, Maybe> project() { + return match(a -> tuple(just(a), nothing(), nothing(), nothing(), nothing(), nothing(), nothing()), + b -> tuple(nothing(), just(b), nothing(), nothing(), nothing(), nothing(), nothing()), + c -> tuple(nothing(), nothing(), just(c), nothing(), nothing(), nothing(), nothing()), + d -> tuple(nothing(), nothing(), nothing(), just(d), nothing(), nothing(), nothing()), + e -> tuple(nothing(), nothing(), nothing(), nothing(), just(e), nothing(), nothing()), + f -> tuple(nothing(), nothing(), nothing(), nothing(), nothing(), just(f), nothing()), + g -> tuple(nothing(), nothing(), nothing(), nothing(), nothing(), nothing(), just(g))); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the first slot value. + * + * @return an optional value representing the projection of the "a" type index + */ + default Maybe projectA() { + return project()._1(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the second slot value. + * + * @return an optional value representing the projection of the "b" type index + */ + default Maybe projectB() { + return project()._2(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the third slot value. + * + * @return an optional value representing the projection of the "c" type index + */ + default Maybe projectC() { + return project()._3(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the fourth slot value. + * + * @return an optional value representing the projection of the "d" type index + */ + default Maybe projectD() { + return project()._4(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the fifth slot value. + * + * @return an optional value representing the projection of the "e" type index + */ + default Maybe projectE() { + return project()._5(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the sixth slot value. + * + * @return an optional value representing the projection of the "f" type index + */ + default Maybe projectF() { + return project()._6(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the seventh slot value. + * + * @return an optional value representing the projection of the "g" type index + */ + default Maybe projectG() { + return project()._7(); + } + + /** + * Embed this coproduct inside another value; that is, given morphisms from this coproduct to R, apply + * the appropriate morphism to this coproduct as a whole. Like {@link CoProduct7#match}, but without unwrapping the + * value. + * + * @param aFn morphism A v B v C v D v E v F v G -> R, applied in the A case + * @param bFn morphism A v B v C v D v E v F v G -> R, applied in the B case + * @param cFn morphism A v B v C v D v E v F v G -> R, applied in the C case + * @param dFn morphism A v B v C v D v E v F v G -> R, applied in the D case + * @param eFn morphism A v B v C v D v E v F v G -> R, applied in the E case + * @param fFn morphism A v B v C v D v E v F v G -> R, applied in the F case + * @param gFn morphism A v B v C v D v E v F v G -> R, applied in the G case + * @param result type + * @return the result of applying the appropriate morphism to this coproduct + */ + @SuppressWarnings("unchecked") + default R embed(Function aFn, + Function bFn, + Function cFn, + Function dFn, + Function eFn, + Function fFn, + Function gFn) { + return this.>match(constantly(fn1(aFn)), + constantly(fn1(bFn)), + constantly(fn1(cFn)), + constantly(fn1(dFn)), + constantly(fn1(eFn)), + constantly(fn1(fFn)), + constantly(fn1(gFn))) + .apply((CP7) this); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct8.java b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct8.java new file mode 100644 index 000000000..3a55b2c24 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct8.java @@ -0,0 +1,192 @@ +package com.jnape.palatable.lambda.adt.coproduct; + +import com.jnape.palatable.lambda.adt.Maybe; +import com.jnape.palatable.lambda.adt.choice.Choice7; +import com.jnape.palatable.lambda.adt.hlist.Tuple8; +import com.jnape.palatable.lambda.functions.Fn1; + +import java.util.function.Function; + +import static com.jnape.palatable.lambda.adt.Maybe.just; +import static com.jnape.palatable.lambda.adt.Maybe.nothing; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.Fn1.fn1; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Constantly.constantly; + +/** + * A generalization of the coproduct of eight types. + * + * @param the first possible type + * @param the second possible type + * @param the third possible type + * @param the fourth possible type + * @param the fifth possible type + * @param the sixth possible type + * @param the seventh possible type + * @param the eighth possible type + * @see CoProduct2 + */ +@FunctionalInterface +public interface CoProduct8> { + + /** + * Type-safe convergence requiring a match against all potential types. + * + * @param aFn morphism A -> R + * @param bFn morphism B -> R + * @param cFn morphism C -> R + * @param dFn morphism D -> R + * @param eFn morphism E -> R + * @param fFn morphism F -> R + * @param gFn morphism G -> R + * @param hFn morphism H -> R + * @param result type + * @return the result of applying the appropriate morphism from whichever type is represented by this coproduct to R + * @see CoProduct2#match(Function, Function) + */ + R match(Function aFn, + Function bFn, + Function cFn, + Function dFn, + Function eFn, + Function fFn, + Function gFn, + Function hFn); + + /** + * Converge this coproduct down to a lower order coproduct by mapping the last possible type into an earlier + * possible type. + * + * @param convergenceFn morphism G -> {@link CoProduct6}<A, B, C, D, E, F, G> + * @return a {@link CoProduct7}<A, B, C, D, E, F, G> + */ + default CoProduct7> converge( + Function> convergenceFn) { + return match(Choice7::a, Choice7::b, Choice7::c, Choice7::d, Choice7::e, Choice7::f, Choice7::g, convergenceFn::apply); + } + + /** + * Project this coproduct onto a tuple. + * + * @return a tuple of the coproduct projection + * @see CoProduct2#project() + */ + default Tuple8, Maybe, Maybe, Maybe, Maybe, Maybe, Maybe, Maybe> project() { + return match(a -> tuple(just(a), nothing(), nothing(), nothing(), nothing(), nothing(), nothing(), nothing()), + b -> tuple(nothing(), just(b), nothing(), nothing(), nothing(), nothing(), nothing(), nothing()), + c -> tuple(nothing(), nothing(), just(c), nothing(), nothing(), nothing(), nothing(), nothing()), + d -> tuple(nothing(), nothing(), nothing(), just(d), nothing(), nothing(), nothing(), nothing()), + e -> tuple(nothing(), nothing(), nothing(), nothing(), just(e), nothing(), nothing(), nothing()), + f -> tuple(nothing(), nothing(), nothing(), nothing(), nothing(), just(f), nothing(), nothing()), + g -> tuple(nothing(), nothing(), nothing(), nothing(), nothing(), nothing(), just(g), nothing()), + h -> tuple(nothing(), nothing(), nothing(), nothing(), nothing(), nothing(), nothing(), just(h))); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the first slot value. + * + * @return an optional value representing the projection of the "a" type index + */ + default Maybe projectA() { + return project()._1(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the second slot value. + * + * @return an optional value representing the projection of the "b" type index + */ + default Maybe projectB() { + return project()._2(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the third slot value. + * + * @return an optional value representing the projection of the "c" type index + */ + default Maybe projectC() { + return project()._3(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the fourth slot value. + * + * @return an optional value representing the projection of the "d" type index + */ + default Maybe projectD() { + return project()._4(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the fifth slot value. + * + * @return an optional value representing the projection of the "e" type index + */ + default Maybe projectE() { + return project()._5(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the sixth slot value. + * + * @return an optional value representing the projection of the "f" type index + */ + default Maybe projectF() { + return project()._6(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the seventh slot value. + * + * @return an optional value representing the projection of the "g" type index + */ + default Maybe projectG() { + return project()._7(); + } + + /** + * Convenience method for projecting this coproduct onto a tuple and then extracting the eighth slot value. + * + * @return an optional value representing the projection of the "h" type index + */ + default Maybe projectH() { + return project()._8(); + } + + /** + * Embed this coproduct inside another value; that is, given morphisms from this coproduct to R, apply + * the appropriate morphism to this coproduct as a whole. Like {@link CoProduct8#match}, but without unwrapping the + * value. + * + * @param aFn morphism A v B v C v D v E v F v G v H -> R, applied in the A case + * @param bFn morphism A v B v C v D v E v F v G v H -> R, applied in the B case + * @param cFn morphism A v B v C v D v E v F v G v H -> R, applied in the C case + * @param dFn morphism A v B v C v D v E v F v G v H -> R, applied in the D case + * @param eFn morphism A v B v C v D v E v F v G v H -> R, applied in the E case + * @param fFn morphism A v B v C v D v E v F v G v H -> R, applied in the F case + * @param gFn morphism A v B v C v D v E v F v G v H -> R, applied in the G case + * @param hFn morphism A v B v C v D v E v F v G v H -> R, applied in the H case + * @param result type + * @return the result of applying the appropriate morphism to this coproduct + */ + @SuppressWarnings("unchecked") + default R embed(Function aFn, + Function bFn, + Function cFn, + Function dFn, + Function eFn, + Function fFn, + Function gFn, + Function hFn) { + return this.>match(constantly(fn1(aFn)), + constantly(fn1(bFn)), + constantly(fn1(cFn)), + constantly(fn1(dFn)), + constantly(fn1(eFn)), + constantly(fn1(fFn)), + constantly(fn1(gFn)), + constantly(fn1(hFn))) + .apply((CP8) this); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice5Test.java b/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice5Test.java index a7e9b8db5..d0ea885e5 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice5Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice5Test.java @@ -51,4 +51,13 @@ public void convergeStaysInChoice() { assertEquals(Choice4.d(4d), d.converge(e -> Choice4.b(e.toString()))); assertEquals(Choice4.b("z"), e.converge(e -> Choice4.b(e.toString()))); } + + @Test + public void divergeStaysInChoice() { + assertEquals(Choice6.a(1), a.diverge()); + assertEquals(Choice6.b("two"), b.diverge()); + assertEquals(Choice6.c(true), c.diverge()); + assertEquals(Choice6.d(4D), d.diverge()); + assertEquals(Choice6.e('z'), e.diverge()); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice6Test.java b/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice6Test.java new file mode 100644 index 000000000..2262884b5 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice6Test.java @@ -0,0 +1,73 @@ +package com.jnape.palatable.lambda.adt.choice; + +import com.jnape.palatable.lambda.adt.coproduct.CoProduct5; +import com.jnape.palatable.traitor.annotations.TestTraits; +import com.jnape.palatable.traitor.framework.Subjects; +import com.jnape.palatable.traitor.runners.Traits; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import testsupport.traits.ApplicativeLaws; +import testsupport.traits.BifunctorLaws; +import testsupport.traits.FunctorLaws; +import testsupport.traits.MonadLaws; +import testsupport.traits.TraversableLaws; + +import java.util.function.Function; + +import static com.jnape.palatable.lambda.adt.choice.Choice6.a; +import static com.jnape.palatable.lambda.adt.choice.Choice6.b; +import static com.jnape.palatable.lambda.adt.choice.Choice6.c; +import static com.jnape.palatable.lambda.adt.choice.Choice6.d; +import static com.jnape.palatable.lambda.adt.choice.Choice6.e; +import static com.jnape.palatable.lambda.adt.choice.Choice6.f; +import static com.jnape.palatable.traitor.framework.Subjects.subjects; +import static org.junit.Assert.assertEquals; + +@RunWith(Traits.class) +public class Choice6Test { + + private Choice6 a; + private Choice6 b; + private Choice6 c; + private Choice6 d; + private Choice6 e; + private Choice6 f; + + @Before + public void setUp() { + a = a(1); + b = b("two"); + c = c(true); + d = d(4d); + e = e('z'); + f = f(5L); + } + + @TestTraits({FunctorLaws.class, ApplicativeLaws.class, MonadLaws.class, BifunctorLaws.class, TraversableLaws.class}) + public Subjects> testSubjects() { + return subjects(a("foo"), b(1), c(true), d('a'), e(2d), f(5L)); + } + + @Test + public void convergeStaysInChoice() { + Function> convergenceFn = f -> Choice5.b(f.toString()); + + assertEquals(Choice5.a(1), a.converge(convergenceFn)); + assertEquals(Choice5.b("two"), b.converge(convergenceFn)); + assertEquals(Choice5.c(true), c.converge(convergenceFn)); + assertEquals(Choice5.d(4d), d.converge(convergenceFn)); + assertEquals(Choice5.e('z'), e.converge(convergenceFn)); + assertEquals(Choice5.b("5"), f.converge(convergenceFn)); + } + + @Test + public void divergeStaysInChoice() { + assertEquals(Choice7.a(1), a.diverge()); + assertEquals(Choice7.b("two"), b.diverge()); + assertEquals(Choice7.c(true), c.diverge()); + assertEquals(Choice7.d(4D), d.diverge()); + assertEquals(Choice7.e('z'), e.diverge()); + assertEquals(Choice7.f(5L), f.diverge()); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice7Test.java b/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice7Test.java new file mode 100644 index 000000000..27bf111d0 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice7Test.java @@ -0,0 +1,78 @@ +package com.jnape.palatable.lambda.adt.choice; + +import com.jnape.palatable.lambda.adt.coproduct.CoProduct6; +import com.jnape.palatable.traitor.annotations.TestTraits; +import com.jnape.palatable.traitor.framework.Subjects; +import com.jnape.palatable.traitor.runners.Traits; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import testsupport.traits.ApplicativeLaws; +import testsupport.traits.BifunctorLaws; +import testsupport.traits.FunctorLaws; +import testsupport.traits.MonadLaws; +import testsupport.traits.TraversableLaws; + +import java.util.function.Function; + +import static com.jnape.palatable.lambda.adt.choice.Choice7.a; +import static com.jnape.palatable.lambda.adt.choice.Choice7.b; +import static com.jnape.palatable.lambda.adt.choice.Choice7.c; +import static com.jnape.palatable.lambda.adt.choice.Choice7.d; +import static com.jnape.palatable.lambda.adt.choice.Choice7.e; +import static com.jnape.palatable.lambda.adt.choice.Choice7.f; +import static com.jnape.palatable.lambda.adt.choice.Choice7.g; +import static com.jnape.palatable.traitor.framework.Subjects.subjects; +import static org.junit.Assert.assertEquals; + +@RunWith(Traits.class) +public class Choice7Test { + + private Choice7 a; + private Choice7 b; + private Choice7 c; + private Choice7 d; + private Choice7 e; + private Choice7 f; + private Choice7 g; + + @Before + public void setUp() { + a = a(1); + b = b("two"); + c = c(true); + d = d(4d); + e = e('z'); + f = f(5L); + g = g(6F); + } + + @TestTraits({FunctorLaws.class, ApplicativeLaws.class, MonadLaws.class, BifunctorLaws.class, TraversableLaws.class}) + public Subjects> testSubjects() { + return subjects(a("foo"), b(1), c(true), d('a'), e(2d), f(5L), g(6F)); + } + + @Test + public void convergeStaysInChoice() { + Function> convergenceFn = g -> Choice6.b(g.toString()); + + assertEquals(Choice6.a(1), a.converge(convergenceFn)); + assertEquals(Choice6.b("two"), b.converge(convergenceFn)); + assertEquals(Choice6.c(true), c.converge(convergenceFn)); + assertEquals(Choice6.d(4d), d.converge(convergenceFn)); + assertEquals(Choice6.e('z'), e.converge(convergenceFn)); + assertEquals(Choice6.f(5L), f.converge(convergenceFn)); + assertEquals(Choice6.b("6.0"), g.converge(convergenceFn)); + } + + @Test + public void divergeStaysInChoice() { + assertEquals(Choice8.a(1), a.diverge()); + assertEquals(Choice8.b("two"), b.diverge()); + assertEquals(Choice8.c(true), c.diverge()); + assertEquals(Choice8.d(4D), d.diverge()); + assertEquals(Choice8.e('z'), e.diverge()); + assertEquals(Choice8.f(5L), f.diverge()); + assertEquals(Choice8.g(6F), g.diverge()); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice8Test.java b/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice8Test.java new file mode 100644 index 000000000..24fede69b --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice8Test.java @@ -0,0 +1,71 @@ +package com.jnape.palatable.lambda.adt.choice; + +import com.jnape.palatable.lambda.adt.coproduct.CoProduct7; +import com.jnape.palatable.traitor.annotations.TestTraits; +import com.jnape.palatable.traitor.framework.Subjects; +import com.jnape.palatable.traitor.runners.Traits; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import testsupport.traits.ApplicativeLaws; +import testsupport.traits.BifunctorLaws; +import testsupport.traits.FunctorLaws; +import testsupport.traits.MonadLaws; +import testsupport.traits.TraversableLaws; + +import java.util.function.Function; + +import static com.jnape.palatable.lambda.adt.choice.Choice8.a; +import static com.jnape.palatable.lambda.adt.choice.Choice8.b; +import static com.jnape.palatable.lambda.adt.choice.Choice8.c; +import static com.jnape.palatable.lambda.adt.choice.Choice8.d; +import static com.jnape.palatable.lambda.adt.choice.Choice8.e; +import static com.jnape.palatable.lambda.adt.choice.Choice8.f; +import static com.jnape.palatable.lambda.adt.choice.Choice8.g; +import static com.jnape.palatable.lambda.adt.choice.Choice8.h; +import static com.jnape.palatable.traitor.framework.Subjects.subjects; +import static org.junit.Assert.assertEquals; + +@RunWith(Traits.class) +public class Choice8Test { + + private Choice8 a; + private Choice8 b; + private Choice8 c; + private Choice8 d; + private Choice8 e; + private Choice8 f; + private Choice8 g; + private Choice8 h; + + @Before + public void setUp() { + a = a(1); + b = b("two"); + c = c(true); + d = d(4d); + e = e('z'); + f = f(5L); + g = g(6F); + h = h((short) 7); + } + + @TestTraits({FunctorLaws.class, ApplicativeLaws.class, MonadLaws.class, BifunctorLaws.class, TraversableLaws.class}) + public Subjects> testSubjects() { + return subjects(a("foo"), b(1), c(true), d('a'), e(2d), f(5L), g(6F), h((short) 7)); + } + + @Test + public void convergeStaysInChoice() { + Function> convergenceFn = h -> Choice7.b(h.toString()); + + assertEquals(Choice7.a(1), a.converge(convergenceFn)); + assertEquals(Choice7.b("two"), b.converge(convergenceFn)); + assertEquals(Choice7.c(true), c.converge(convergenceFn)); + assertEquals(Choice7.d(4d), d.converge(convergenceFn)); + assertEquals(Choice7.e('z'), e.converge(convergenceFn)); + assertEquals(Choice7.f(5L), f.converge(convergenceFn)); + assertEquals(Choice7.g(6F), g.converge(convergenceFn)); + assertEquals(Choice7.b("7"), h.converge(convergenceFn)); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3Test.java b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3Test.java index 7bf9f867a..49af8665c 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3Test.java @@ -1,6 +1,8 @@ package com.jnape.palatable.lambda.adt.coproduct; import com.jnape.palatable.lambda.adt.Maybe; +import com.jnape.palatable.lambda.adt.choice.Choice2; +import com.jnape.palatable.lambda.adt.choice.Choice3; import org.junit.Before; import org.junit.Test; @@ -59,27 +61,11 @@ public void diverge() { @Test public void converge() { - Function> convergenceFn = x -> x ? new CoProduct2>() { - @Override - public R match(Function aFn, Function bFn) { - return aFn.apply(-1); - } - } : new CoProduct2>() { - @Override - public R match(Function aFn, Function bFn) { - return bFn.apply("false"); - } - }; + Function> convergenceFn = x -> x ? Choice2.a(-1) : Choice2.b("false"); assertEquals(1, a.converge(convergenceFn).match(id(), id())); assertEquals("two", b.converge(convergenceFn).match(id(), id())); assertEquals(-1, c.converge(convergenceFn).match(id(), id())); - assertEquals("false", new CoProduct3>() { - @Override - public R match(Function aFn, Function bFn, - Function cFn) { - return cFn.apply(false); - } - }.converge(convergenceFn).match(id(), id())); + assertEquals("false", Choice3.c(false).converge(convergenceFn).match(id(), id())); } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4Test.java b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4Test.java index d9f2c1a2e..de58ff042 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4Test.java @@ -1,6 +1,8 @@ package com.jnape.palatable.lambda.adt.coproduct; import com.jnape.palatable.lambda.adt.Maybe; +import com.jnape.palatable.lambda.adt.choice.Choice3; +import com.jnape.palatable.lambda.adt.choice.Choice4; import org.junit.Before; import org.junit.Test; @@ -8,10 +10,6 @@ import static com.jnape.palatable.lambda.adt.Maybe.just; import static com.jnape.palatable.lambda.adt.Maybe.nothing; -import static com.jnape.palatable.lambda.adt.choice.Choice4.a; -import static com.jnape.palatable.lambda.adt.choice.Choice4.b; -import static com.jnape.palatable.lambda.adt.choice.Choice4.c; -import static com.jnape.palatable.lambda.adt.choice.Choice4.d; import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; import static org.junit.Assert.assertEquals; @@ -25,10 +23,34 @@ public class CoProduct4Test { @Before public void setUp() throws Exception { - a = a(1); - b = b("two"); - c = c(true); - d = d(4D); + a = new CoProduct4>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn) { + return aFn.apply(1); + } + }; + b = new CoProduct4>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn) { + return bFn.apply("two"); + } + }; + c = new CoProduct4>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn) { + return cFn.apply(true); + } + }; + d = new CoProduct4>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn) { + return dFn.apply(4D); + } + }; } @Test @@ -49,50 +71,17 @@ public void diverge() { @Test public void converge() { - Function> convergenceFn = x -> x.equals(1d) ? new CoProduct3>() { - @Override - public R match(Function aFn, Function bFn, - Function cFn) { - return aFn.apply(1); - } - } : x.equals(2d) - ? new CoProduct3>() { - @Override - public R match(Function aFn, Function bFn, - Function cFn) { - return bFn.apply("b"); - } - } : new CoProduct3>() { - @Override - public R match(Function aFn, Function bFn, - Function cFn) { - return cFn.apply(false); - } - }; + Function> convergenceFn = x -> x.equals(1d) + ? Choice3.a(1) + : x.equals(2d) + ? Choice3.b("b") + : Choice3.c(false); assertEquals(1, a.converge(convergenceFn).match(id(), id(), id())); assertEquals("two", b.converge(convergenceFn).match(id(), id(), id())); assertEquals(true, c.converge(convergenceFn).match(id(), id(), id())); - assertEquals(1, new CoProduct4>() { - @Override - public R match(Function aFn, Function bFn, - Function cFn, Function dFn) { - return dFn.apply(1d); - } - }.converge(convergenceFn).match(id(), id(), id())); - assertEquals("b", new CoProduct4>() { - @Override - public R match(Function aFn, Function bFn, - Function cFn, Function dFn) { - return dFn.apply(2d); - } - }.converge(convergenceFn).match(id(), id(), id())); - assertEquals(false, new CoProduct4>() { - @Override - public R match(Function aFn, Function bFn, - Function cFn, Function dFn) { - return dFn.apply(3d); - } - }.converge(convergenceFn).match(id(), id(), id())); + assertEquals(1, Choice4.d(1D).converge(convergenceFn).match(id(), id(), id())); + assertEquals("b", Choice4.d(2D).converge(convergenceFn).match(id(), id(), id())); + assertEquals(false, Choice4.d(3D).converge(convergenceFn).match(id(), id(), id())); } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5Test.java b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5Test.java index 93f57a2e0..14cccc369 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5Test.java @@ -1,6 +1,8 @@ package com.jnape.palatable.lambda.adt.coproduct; import com.jnape.palatable.lambda.adt.Maybe; +import com.jnape.palatable.lambda.adt.choice.Choice4; +import com.jnape.palatable.lambda.adt.choice.Choice5; import org.junit.Before; import org.junit.Test; @@ -73,80 +75,32 @@ public void match() { assertEquals('z', e.match(id(), id(), id(), id(), id())); } + @Test + public void diverge() { + assertEquals(1, a.diverge().match(id(), id(), id(), id(), id(), id())); + assertEquals("two", b.diverge().match(id(), id(), id(), id(), id(), id())); + assertEquals(true, c.diverge().match(id(), id(), id(), id(), id(), id())); + assertEquals(4D, d.diverge().match(id(), id(), id(), id(), id(), id())); + assertEquals('z', e.diverge().match(id(), id(), id(), id(), id(), id())); + } + @Test public void converge() { - Function>> convergenceFn = x -> - x.equals('a') ? new CoProduct4>() { - @Override - public R match(Function aFn, - Function bFn, - Function cFn, - Function dFn) { - return aFn.apply(1); - } - } : x.equals('b') - ? new CoProduct4>() { - @Override - public R match(Function aFn, - Function bFn, - Function cFn, - Function dFn) { - return bFn.apply("b"); - } - } : x.equals('c') - ? new CoProduct4>() { - @Override - public R match(Function aFn, - Function bFn, - Function cFn, - Function dFn) { - return cFn.apply(false); - } - } : new CoProduct4>() { - @Override - public R match(Function aFn, - Function bFn, - Function cFn, - Function dFn) { - return dFn.apply(1d); - } - }; + Function> convergenceFn = x -> x.equals('a') + ? Choice4.a(1) + : x.equals('b') + ? Choice4.b("b") + : x.equals('c') + ? Choice4.c(false) + : Choice4.d(1D); assertEquals(1, a.converge(convergenceFn).match(id(), id(), id(), id())); assertEquals("two", b.converge(convergenceFn).match(id(), id(), id(), id())); assertEquals(true, c.converge(convergenceFn).match(id(), id(), id(), id())); assertEquals(4D, d.converge(convergenceFn).match(id(), id(), id(), id())); - assertEquals(1, new CoProduct5>() { - @Override - public R match(Function aFn, Function bFn, - Function cFn, Function dFn, - Function eFn) { - return eFn.apply('a'); - } - }.converge(convergenceFn).match(id(), id(), id(), id())); - assertEquals("b", new CoProduct5>() { - @Override - public R match(Function aFn, Function bFn, - Function cFn, Function dFn, - Function eFn) { - return eFn.apply('b'); - } - }.converge(convergenceFn).match(id(), id(), id(), id())); - assertEquals(false, new CoProduct5>() { - @Override - public R match(Function aFn, Function bFn, - Function cFn, Function dFn, - Function eFn) { - return eFn.apply('c'); - } - }.converge(convergenceFn).match(id(), id(), id(), id())); - assertEquals(1d, new CoProduct5>() { - @Override - public R match(Function aFn, Function bFn, - Function cFn, Function dFn, - Function eFn) { - return eFn.apply('d'); - } - }.converge(convergenceFn).match(id(), id(), id(), id())); + assertEquals(1, Choice5.e('a').converge(convergenceFn).match(id(), id(), id(), id())); + assertEquals("b", Choice5.e('b').converge(convergenceFn).match(id(), id(), id(), id())); + assertEquals(false, Choice5.e('c').converge(convergenceFn).match(id(), id(), id(), id())); + assertEquals(1d, Choice5.e('d').converge(convergenceFn).match(id(), id(), id(), id())); } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct6Test.java b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct6Test.java new file mode 100644 index 000000000..8c38290e7 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct6Test.java @@ -0,0 +1,148 @@ +package com.jnape.palatable.lambda.adt.coproduct; + +import com.jnape.palatable.lambda.adt.Maybe; +import com.jnape.palatable.lambda.adt.choice.Choice5; +import com.jnape.palatable.lambda.adt.choice.Choice6; +import org.junit.Before; +import org.junit.Test; + +import java.util.function.Function; + +import static com.jnape.palatable.lambda.adt.Maybe.just; +import static com.jnape.palatable.lambda.adt.Maybe.nothing; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; +import static org.junit.Assert.assertEquals; + +public class CoProduct6Test { + + private CoProduct6 a; + private CoProduct6 b; + private CoProduct6 c; + private CoProduct6 d; + private CoProduct6 e; + private CoProduct6 f; + + @Before + public void setUp() { + a = new CoProduct6>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn) { + return aFn.apply(1); + } + }; + b = new CoProduct6>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn) { + return bFn.apply("two"); + } + }; + c = new CoProduct6>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn) { + return cFn.apply(true); + } + }; + d = new CoProduct6>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn) { + return dFn.apply(4D); + } + }; + e = new CoProduct6>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn) { + return eFn.apply('z'); + } + }; + f = new CoProduct6>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn) { + return fFn.apply(5L); + } + }; + } + + @Test + public void match() { + assertEquals(1, a.match(id(), id(), id(), id(), id(), id())); + assertEquals("two", b.match(id(), id(), id(), id(), id(), id())); + assertEquals(true, c.match(id(), id(), id(), id(), id(), id())); + assertEquals(4D, d.match(id(), id(), id(), id(), id(), id())); + assertEquals('z', e.match(id(), id(), id(), id(), id(), id())); + assertEquals(5L, f.match(id(), id(), id(), id(), id(), id())); + } + + @Test + public void diverge() { + assertEquals(1, a.diverge().match(id(), id(), id(), id(), id(), id(), id())); + assertEquals("two", b.diverge().match(id(), id(), id(), id(), id(), id(), id())); + assertEquals(true, c.diverge().match(id(), id(), id(), id(), id(), id(), id())); + assertEquals(4D, d.diverge().match(id(), id(), id(), id(), id(), id(), id())); + assertEquals('z', e.diverge().match(id(), id(), id(), id(), id(), id(), id())); + assertEquals(5L, f.diverge().match(id(), id(), id(), id(), id(), id(), id())); + } + + @Test + public void converge() { + Function> convergenceFn = x -> + x.equals(1L) + ? Choice5.a(1) + : x.equals(2L) + ? Choice5.b("b") + : x.equals(3L) + ? Choice5.c(false) + : x.equals(4L) + ? Choice5.d(1D) + : Choice5.e('a'); + assertEquals(1, a.converge(convergenceFn).match(id(), id(), id(), id(), id())); + assertEquals("two", b.converge(convergenceFn).match(id(), id(), id(), id(), id())); + assertEquals(true, c.converge(convergenceFn).match(id(), id(), id(), id(), id())); + assertEquals(4D, d.converge(convergenceFn).match(id(), id(), id(), id(), id())); + assertEquals('z', e.converge(convergenceFn).match(id(), id(), id(), id(), id())); + assertEquals(1, Choice6.f(1L).converge(convergenceFn).match(id(), id(), id(), id(), id())); + assertEquals("b", Choice6.f(2L).converge(convergenceFn).match(id(), id(), id(), id(), id())); + assertEquals(false, Choice6.f(3L).converge(convergenceFn).match(id(), id(), id(), id(), id())); + assertEquals(1d, Choice6.f(4L).converge(convergenceFn).match(id(), id(), id(), id(), id())); + assertEquals('a', Choice6.f(5L).converge(convergenceFn).match(id(), id(), id(), id(), id())); + } + + @Test + public void projections() { + assertEquals(tuple(just(1), nothing(), nothing(), nothing(), nothing(), nothing()), a.project()); + assertEquals(tuple(nothing(), just("two"), nothing(), nothing(), nothing(), nothing()), b.project()); + assertEquals(tuple(nothing(), nothing(), just(true), nothing(), nothing(), nothing()), c.project()); + assertEquals(tuple(nothing(), nothing(), nothing(), just(4D), nothing(), nothing()), d.project()); + assertEquals(tuple(nothing(), nothing(), nothing(), nothing(), just('z'), nothing()), e.project()); + assertEquals(tuple(nothing(), nothing(), nothing(), nothing(), nothing(), just(5L)), f.project()); + + assertEquals(tuple(a.projectA(), a.projectB(), a.projectC(), a.projectD(), a.projectE(), a.projectF()), a.project()); + assertEquals(tuple(b.projectA(), b.projectB(), b.projectC(), b.projectD(), b.projectE(), b.projectF()), b.project()); + assertEquals(tuple(c.projectA(), c.projectB(), c.projectC(), c.projectD(), c.projectE(), c.projectF()), c.project()); + assertEquals(tuple(d.projectA(), d.projectB(), d.projectC(), d.projectD(), d.projectE(), d.projectF()), d.project()); + assertEquals(tuple(e.projectA(), e.projectB(), e.projectC(), e.projectD(), e.projectE(), e.projectF()), e.project()); + assertEquals(tuple(f.projectA(), f.projectB(), f.projectC(), f.projectD(), f.projectE(), f.projectF()), f.project()); + } + + @Test + public void embed() { + assertEquals(just(a), a.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + assertEquals(just(b), b.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + assertEquals(just(c), c.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + assertEquals(just(d), d.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + assertEquals(just(e), e.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + assertEquals(just(f), f.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct7Test.java b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct7Test.java new file mode 100644 index 000000000..1ae73c596 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct7Test.java @@ -0,0 +1,173 @@ +package com.jnape.palatable.lambda.adt.coproduct; + +import com.jnape.palatable.lambda.adt.Maybe; +import com.jnape.palatable.lambda.adt.choice.Choice6; +import com.jnape.palatable.lambda.adt.choice.Choice7; +import org.junit.Before; +import org.junit.Test; + +import java.util.function.Function; + +import static com.jnape.palatable.lambda.adt.Maybe.just; +import static com.jnape.palatable.lambda.adt.Maybe.nothing; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; +import static org.junit.Assert.assertEquals; + +public class CoProduct7Test { + + private CoProduct7 a; + private CoProduct7 b; + private CoProduct7 c; + private CoProduct7 d; + private CoProduct7 e; + private CoProduct7 f; + private CoProduct7 g; + + @Before + public void setUp() { + a = new CoProduct7>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn) { + return aFn.apply(1); + } + }; + b = new CoProduct7>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn) { + return bFn.apply("two"); + } + }; + c = new CoProduct7>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn) { + return cFn.apply(true); + } + }; + d = new CoProduct7>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn) { + return dFn.apply(4D); + } + }; + e = new CoProduct7>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn) { + return eFn.apply('z'); + } + }; + f = new CoProduct7>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn) { + return fFn.apply(5L); + } + }; + g = new CoProduct7>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn) { + return gFn.apply(6f); + } + }; + } + + @Test + public void match() { + assertEquals(1, a.match(id(), id(), id(), id(), id(), id(), id())); + assertEquals("two", b.match(id(), id(), id(), id(), id(), id(), id())); + assertEquals(true, c.match(id(), id(), id(), id(), id(), id(), id())); + assertEquals(4D, d.match(id(), id(), id(), id(), id(), id(), id())); + assertEquals('z', e.match(id(), id(), id(), id(), id(), id(), id())); + assertEquals(5L, f.match(id(), id(), id(), id(), id(), id(), id())); + assertEquals(6f, g.match(id(), id(), id(), id(), id(), id(), id())); + } + + @Test + public void diverge() { + assertEquals(1, a.diverge().match(id(), id(), id(), id(), id(), id(), id(), id())); + assertEquals("two", b.diverge().match(id(), id(), id(), id(), id(), id(), id(), id())); + assertEquals(true, c.diverge().match(id(), id(), id(), id(), id(), id(), id(), id())); + assertEquals(4D, d.diverge().match(id(), id(), id(), id(), id(), id(), id(), id())); + assertEquals('z', e.diverge().match(id(), id(), id(), id(), id(), id(), id(), id())); + assertEquals(5L, f.diverge().match(id(), id(), id(), id(), id(), id(), id(), id())); + assertEquals(6F, g.diverge().match(id(), id(), id(), id(), id(), id(), id(), id())); + } + + @Test + public void converge() { + Function> convergenceFn = x -> + x.equals(1f) + ? Choice6.a(1) + : x.equals(2f) + ? Choice6.b("b") + : x.equals(3f) + ? Choice6.c(false) + : x.equals(4f) + ? Choice6.d(1D) + : x.equals(5f) + ? Choice6.e('a') + : Choice6.f(5L); + assertEquals(1, a.converge(convergenceFn).match(id(), id(), id(), id(), id(), id())); + assertEquals("two", b.converge(convergenceFn).match(id(), id(), id(), id(), id(), id())); + assertEquals(true, c.converge(convergenceFn).match(id(), id(), id(), id(), id(), id())); + assertEquals(4D, d.converge(convergenceFn).match(id(), id(), id(), id(), id(), id())); + assertEquals('z', e.converge(convergenceFn).match(id(), id(), id(), id(), id(), id())); + assertEquals(5L, f.converge(convergenceFn).match(id(), id(), id(), id(), id(), id())); + assertEquals(1, Choice7.g(1F).converge(convergenceFn).match(id(), id(), id(), id(), id(), id())); + assertEquals("b", Choice7.g(2F).converge(convergenceFn).match(id(), id(), id(), id(), id(), id())); + assertEquals(false, Choice7.g(3F).converge(convergenceFn).match(id(), id(), id(), id(), id(), id())); + assertEquals(1d, Choice7.g(4F).converge(convergenceFn).match(id(), id(), id(), id(), id(), id())); + assertEquals('a', Choice7.g(5F).converge(convergenceFn).match(id(), id(), id(), id(), id(), id())); + assertEquals(5L, Choice7.g(6F).converge(convergenceFn).match(id(), id(), id(), id(), id(), id())); + } + + @Test + public void projections() { + assertEquals(tuple(just(1), nothing(), nothing(), nothing(), nothing(), nothing(), nothing()), a.project()); + assertEquals(tuple(nothing(), just("two"), nothing(), nothing(), nothing(), nothing(), nothing()), b.project()); + assertEquals(tuple(nothing(), nothing(), just(true), nothing(), nothing(), nothing(), nothing()), c.project()); + assertEquals(tuple(nothing(), nothing(), nothing(), just(4D), nothing(), nothing(), nothing()), d.project()); + assertEquals(tuple(nothing(), nothing(), nothing(), nothing(), just('z'), nothing(), nothing()), e.project()); + assertEquals(tuple(nothing(), nothing(), nothing(), nothing(), nothing(), just(5L), nothing()), f.project()); + assertEquals(tuple(nothing(), nothing(), nothing(), nothing(), nothing(), nothing(), just(6F)), g.project()); + + assertEquals(tuple(a.projectA(), a.projectB(), a.projectC(), a.projectD(), a.projectE(), a.projectF(), a.projectG()), a.project()); + assertEquals(tuple(b.projectA(), b.projectB(), b.projectC(), b.projectD(), b.projectE(), b.projectF(), b.projectG()), b.project()); + assertEquals(tuple(c.projectA(), c.projectB(), c.projectC(), c.projectD(), c.projectE(), c.projectF(), c.projectG()), c.project()); + assertEquals(tuple(d.projectA(), d.projectB(), d.projectC(), d.projectD(), d.projectE(), d.projectF(), d.projectG()), d.project()); + assertEquals(tuple(e.projectA(), e.projectB(), e.projectC(), e.projectD(), e.projectE(), e.projectF(), e.projectG()), e.project()); + assertEquals(tuple(f.projectA(), f.projectB(), f.projectC(), f.projectD(), f.projectE(), f.projectF(), f.projectG()), f.project()); + assertEquals(tuple(g.projectA(), g.projectB(), g.projectC(), g.projectD(), g.projectE(), g.projectF(), g.projectG()), g.project()); + } + + @Test + public void embed() { + assertEquals(just(a), a.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + assertEquals(just(b), b.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + assertEquals(just(c), c.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + assertEquals(just(d), d.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + assertEquals(just(e), e.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + assertEquals(just(f), f.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + assertEquals(just(g), g.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct8Test.java b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct8Test.java new file mode 100644 index 000000000..e85f3152a --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct8Test.java @@ -0,0 +1,180 @@ +package com.jnape.palatable.lambda.adt.coproduct; + +import com.jnape.palatable.lambda.adt.Maybe; +import com.jnape.palatable.lambda.adt.choice.Choice7; +import com.jnape.palatable.lambda.adt.choice.Choice8; +import org.junit.Before; +import org.junit.Test; + +import java.util.function.Function; + +import static com.jnape.palatable.lambda.adt.Maybe.just; +import static com.jnape.palatable.lambda.adt.Maybe.nothing; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; +import static org.junit.Assert.assertEquals; + +public class CoProduct8Test { + + private CoProduct8 a; + private CoProduct8 b; + private CoProduct8 c; + private CoProduct8 d; + private CoProduct8 e; + private CoProduct8 f; + private CoProduct8 g; + private CoProduct8 h; + + @Before + public void setUp() { + a = new CoProduct8>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn, Function hFn) { + return aFn.apply(1); + } + }; + b = new CoProduct8>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn, Function hFn) { + return bFn.apply("two"); + } + }; + c = new CoProduct8>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn, Function hFn) { + return cFn.apply(true); + } + }; + d = new CoProduct8>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn, Function hFn) { + return dFn.apply(4D); + } + }; + e = new CoProduct8>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn, Function hFn) { + return eFn.apply('z'); + } + }; + f = new CoProduct8>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn, Function hFn) { + return fFn.apply(5L); + } + }; + g = new CoProduct8>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn, Function hFn) { + return gFn.apply(6f); + } + }; + h = new CoProduct8>() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn, Function fFn, + Function gFn, Function hFn) { + return hFn.apply((short) 7); + } + }; + } + + @Test + public void match() { + assertEquals(1, a.match(id(), id(), id(), id(), id(), id(), id(), id())); + assertEquals("two", b.match(id(), id(), id(), id(), id(), id(), id(), id())); + assertEquals(true, c.match(id(), id(), id(), id(), id(), id(), id(), id())); + assertEquals(4D, d.match(id(), id(), id(), id(), id(), id(), id(), id())); + assertEquals('z', e.match(id(), id(), id(), id(), id(), id(), id(), id())); + assertEquals(5L, f.match(id(), id(), id(), id(), id(), id(), id(), id())); + assertEquals(6f, g.match(id(), id(), id(), id(), id(), id(), id(), id())); + assertEquals((short) 7, h.match(id(), id(), id(), id(), id(), id(), id(), id())); + } + + @Test + public void converge() { + Function> convergenceFn = x -> + x.equals((short) 1) + ? Choice7.a(1) + : x.equals((short) 2) + ? Choice7.b("b") + : x.equals((short) 3) + ? Choice7.c(false) + : x.equals((short) 4) + ? Choice7.d(1D) + : x.equals((short) 5) + ? Choice7.e('a') + : x.equals((short) 6) + ? Choice7.f(5L) + : Choice7.g(6F); + assertEquals(1, a.converge(convergenceFn).match(id(), id(), id(), id(), id(), id(), id())); + assertEquals("two", b.converge(convergenceFn).match(id(), id(), id(), id(), id(), id(), id())); + assertEquals(true, c.converge(convergenceFn).match(id(), id(), id(), id(), id(), id(), id())); + assertEquals(4D, d.converge(convergenceFn).match(id(), id(), id(), id(), id(), id(), id())); + assertEquals('z', e.converge(convergenceFn).match(id(), id(), id(), id(), id(), id(), id())); + assertEquals(5L, f.converge(convergenceFn).match(id(), id(), id(), id(), id(), id(), id())); + assertEquals(6F, g.converge(convergenceFn).match(id(), id(), id(), id(), id(), id(), id())); + assertEquals(1, Choice8.h((short) 1).converge(convergenceFn).match(id(), id(), id(), id(), id(), id(), id())); + assertEquals("b", Choice8.h((short) 2).converge(convergenceFn).match(id(), id(), id(), id(), id(), id(), id())); + assertEquals(false, Choice8.h((short) 3).converge(convergenceFn).match(id(), id(), id(), id(), id(), id(), id())); + assertEquals(1d, Choice8.h((short) 4).converge(convergenceFn).match(id(), id(), id(), id(), id(), id(), id())); + assertEquals('a', Choice8.h((short) 5).converge(convergenceFn).match(id(), id(), id(), id(), id(), id(), id())); + assertEquals(5L, Choice8.h((short) 6).converge(convergenceFn).match(id(), id(), id(), id(), id(), id(), id())); + assertEquals(6F, Choice8.h((short) 7).converge(convergenceFn).match(id(), id(), id(), id(), id(), id(), id())); + } + + @Test + public void projections() { + assertEquals(tuple(just(1), nothing(), nothing(), nothing(), nothing(), nothing(), nothing(), nothing()), a.project()); + assertEquals(tuple(nothing(), just("two"), nothing(), nothing(), nothing(), nothing(), nothing(), nothing()), b.project()); + assertEquals(tuple(nothing(), nothing(), just(true), nothing(), nothing(), nothing(), nothing(), nothing()), c.project()); + assertEquals(tuple(nothing(), nothing(), nothing(), just(4D), nothing(), nothing(), nothing(), nothing()), d.project()); + assertEquals(tuple(nothing(), nothing(), nothing(), nothing(), just('z'), nothing(), nothing(), nothing()), e.project()); + assertEquals(tuple(nothing(), nothing(), nothing(), nothing(), nothing(), just(5L), nothing(), nothing()), f.project()); + assertEquals(tuple(nothing(), nothing(), nothing(), nothing(), nothing(), nothing(), just(6F), nothing()), g.project()); + assertEquals(tuple(nothing(), nothing(), nothing(), nothing(), nothing(), nothing(), nothing(), just((short) 7)), h.project()); + + assertEquals(tuple(a.projectA(), a.projectB(), a.projectC(), a.projectD(), a.projectE(), a.projectF(), a.projectG(), a.projectH()), a.project()); + assertEquals(tuple(b.projectA(), b.projectB(), b.projectC(), b.projectD(), b.projectE(), b.projectF(), b.projectG(), b.projectH()), b.project()); + assertEquals(tuple(c.projectA(), c.projectB(), c.projectC(), c.projectD(), c.projectE(), c.projectF(), c.projectG(), c.projectH()), c.project()); + assertEquals(tuple(d.projectA(), d.projectB(), d.projectC(), d.projectD(), d.projectE(), d.projectF(), d.projectG(), d.projectH()), d.project()); + assertEquals(tuple(e.projectA(), e.projectB(), e.projectC(), e.projectD(), e.projectE(), e.projectF(), e.projectG(), e.projectH()), e.project()); + assertEquals(tuple(f.projectA(), f.projectB(), f.projectC(), f.projectD(), f.projectE(), f.projectF(), f.projectG(), f.projectH()), f.project()); + assertEquals(tuple(g.projectA(), g.projectB(), g.projectC(), g.projectD(), g.projectE(), g.projectF(), g.projectG(), g.projectH()), g.project()); + assertEquals(tuple(h.projectA(), h.projectB(), h.projectC(), h.projectD(), h.projectE(), h.projectF(), h.projectG(), h.projectH()), h.project()); + } + + @Test + public void embed() { + assertEquals(just(a), a.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + assertEquals(just(b), b.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + assertEquals(just(c), c.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + assertEquals(just(d), d.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + assertEquals(just(e), e.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + assertEquals(just(f), f.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + assertEquals(just(g), g.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + assertEquals(just(h), h.embed(Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just, Maybe::just)); + } +} \ No newline at end of file From 0fc97a976676fc6768e349e6932c0498b188a1ff Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 26 Nov 2017 15:50:17 -0600 Subject: [PATCH 08/24] Adding Maybe treatment to README --- README.md | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3049495b0..c6635009d 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,8 @@ Functional patterns for Java 8 - [Monads](#monads) - [Traversables](#traversables) - [ADTs](#adts) - - [HLists](#hlists) + - [Maybe](#maybe) + - [HList](#hlist) - [Tuples](#tuples) - [HMaps](#hmaps) - [CoProducts](#coproducts) @@ -455,6 +456,56 @@ As always, there are [some laws](https://hackage.haskell.org/package/base-4.9.1. Lambda also supports a few first-class [algebraic data types](https://www.wikiwand.com/en/Algebraic_data_type). +### Maybe + +`Maybe` is the _lambda_ analog to `java.util.Optional`. It behaves in much of the same way as `j.u.Optional`, except that it quite intentionally does not support the inherently unsafe `j.u.Optional#get`. + +```Java +Maybe maybeInt = Maybe.just(1); // Just 1 +Maybe maybeString = Maybe.nothing(); // Nothing +``` + +Also, because it's a _lambda_ type, it takes advantage of the full functor hierarchy, as well as some helpful conversion functions: + +```Java +Maybe just = Maybe.maybe("string"); // Just "string" +Maybe nothing = Maybe.maybe(null); // Nothing + +Maybe maybeX = Maybe.just(1); +Maybe maybeY = Maybe.just(2); + +maybeY.zip(maybeX.fmap(x -> y -> x + y)); // Just 3 +maybeY.zip(nothing()); // Nothing +Maybe.nothing().zip(maybeX.fmap(x -> y -> x + y)); // Nothing + +Either right = maybeX.toEither(() -> "was empty"); // Right 1 +Either left = Maybe.nothing().toEither(() -> "was empty"); // Left "was empty" + +Maybe.fromEither(right); // Just 1 +Maybe.fromEither(left); // Nothing +``` + +Finally, for compatibility purposes, `Maybe` and `j.u.Optional` can be trivially converted back and forth: + +```Java +Maybe just1 = Maybe.just(1); // Just 1 +Optional present1 = just1.toOptional(); // Optional.of(1) + +Optional empty = Optional.empty(); // Optional.empty() +Maybe nothing = Maybe.fromOptional(empty); // Nothing +``` + +***Note***: One compatibility difference between `j.u.Optional` and `Maybe` is how `map`/`fmap` behave regarding functions that return `null`: `j.u.Optional` re-wraps `null` results from `map` operations in another `j.u.Optional`, whereas `Maybe` considers this to be an error, and throws an exception. The reason `Maybe` throws in this case is because `fmap` is not an operation to be called speculatively, and so any function that returns `null` in the context of an `fmap` operation is considered to be erroneous. Instead of calling `fmap` with a function that might return `null`, the function result should be wrapped in a `Maybe` and `flatMap` should be used, as illustrated in the following example: + +```Java +Function nullResultFn = __ -> null; + +Optional.of(1).map(nullResultFn); // Optional.empty() +Maybe.just(1).fmap(nullResultFn); // throws NullPointerException + +Maybe.just(1).flatMap(nullResultFn.andThen(Maybe::maybe)); // Nothing +``` + ### Heterogeneous Lists (HLists) HLists are type-safe heterogeneous lists, meaning they can store elements of different types in the same list while facilitating certain type-safe interactions. From 550792233f4d35a000ae27eeca8514abd8baa727 Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 26 Nov 2017 19:41:35 -0600 Subject: [PATCH 09/24] Adding Into3 through Into8 --- CHANGELOG.md | 3 ++ .../lambda/functions/builtin/fn2/Into3.java | 38 +++++++++++++++ .../lambda/functions/builtin/fn2/Into4.java | 39 ++++++++++++++++ .../lambda/functions/builtin/fn2/Into5.java | 40 ++++++++++++++++ .../lambda/functions/builtin/fn2/Into6.java | 42 +++++++++++++++++ .../lambda/functions/builtin/fn2/Into7.java | 43 +++++++++++++++++ .../lambda/functions/builtin/fn2/Into8.java | 46 +++++++++++++++++++ .../functions/builtin/fn2/Into3Test.java | 15 ++++++ .../functions/builtin/fn2/Into4Test.java | 15 ++++++ .../functions/builtin/fn2/Into5Test.java | 15 ++++++ .../functions/builtin/fn2/Into6Test.java | 15 ++++++ .../functions/builtin/fn2/Into7Test.java | 15 ++++++ .../functions/builtin/fn2/Into8Test.java | 15 ++++++ 13 files changed, 341 insertions(+) create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into3.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into4.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into5.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into6.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into7.java create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into8.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into3Test.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into4Test.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into5Test.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into6Test.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into7Test.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into8Test.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b2a1c652..314f17ff6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `Fn5` through `Fn8` - `Tuple5#into` - `Tuple6` through `Tuple8` +- `CoProduct6` through `CoProduct8` and `Choice6` through `Choice8` +- `CoProduct5#diverge` and `Choice5#diverge` +- `Into3` through `Into8`, for applying a `Tuple*` to an `Fn*` ### Removed - `Either#toOptional`, deprecated in previous release diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into3.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into3.java new file mode 100644 index 000000000..8cbe3b75e --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into3.java @@ -0,0 +1,38 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import com.jnape.palatable.lambda.adt.hlist.Tuple3; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.Fn2; +import com.jnape.palatable.lambda.functions.Fn3; + +/** + * Given an {@link Fn3}<A, B, C, D> and a {@link Tuple3}<A, B, C>, destructure the + * tuple and apply the slots as arguments to the function, returning the result. + * + * @param the first argument type + * @param the second argument type + * @param the third argument type + * @param the result type + */ +public final class Into3 implements Fn2, Tuple3, D> { + + private static final Into3 INSTANCE = new Into3(); + + @Override + public D apply(Fn3 fn, Tuple3 tuple) { + return tuple.into(fn); + } + + @SuppressWarnings("unchecked") + public static Into3 into3() { + return INSTANCE; + } + + public static Fn1, D> into3(Fn3 fn) { + return Into3.into3().apply(fn); + } + + public static D into3(Fn3 fn, Tuple3 tuple) { + return into3(fn).apply(tuple); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into4.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into4.java new file mode 100644 index 000000000..ec879df81 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into4.java @@ -0,0 +1,39 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import com.jnape.palatable.lambda.adt.hlist.Tuple4; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.Fn2; +import com.jnape.palatable.lambda.functions.Fn4; + +/** + * Given an {@link Fn4}<A, B, C, D, E> and a {@link Tuple4}<A, B, C, D>, + * destructure the tuple and apply the slots as arguments to the function, returning the result. + * + * @param the first argument type + * @param the second argument type + * @param the third argument type + * @param the fourth argument type + * @param the result type + */ +public final class Into4 implements Fn2, Tuple4, E> { + + private static final Into4 INSTANCE = new Into4(); + + @Override + public E apply(Fn4 fn, Tuple4 tuple) { + return tuple.into(fn); + } + + @SuppressWarnings("unchecked") + public static Into4 into4() { + return INSTANCE; + } + + public static Fn1, E> into4(Fn4 fn) { + return Into4.into4().apply(fn); + } + + public static E into4(Fn4 fn, Tuple4 tuple) { + return into4(fn).apply(tuple); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into5.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into5.java new file mode 100644 index 000000000..91fc50a3a --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into5.java @@ -0,0 +1,40 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import com.jnape.palatable.lambda.adt.hlist.Tuple5; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.Fn2; +import com.jnape.palatable.lambda.functions.Fn5; + +/** + * Given an {@link Fn5}<A, B, C, D, E, F> and a {@link Tuple5}<A, B, C, D, E>, + * destructure the tuple and apply the slots as arguments to the function, returning the result. + * + * @param the first argument type + * @param the second argument type + * @param the third argument type + * @param the fourth argument type + * @param the fifth argument type + * @param the result type + */ +public final class Into5 implements Fn2, Tuple5, F> { + + private static final Into5 INSTANCE = new Into5(); + + @Override + public F apply(Fn5 fn, Tuple5 tuple) { + return tuple.into(fn); + } + + @SuppressWarnings("unchecked") + public static Into5 into5() { + return INSTANCE; + } + + public static Fn1, F> into5(Fn5 fn) { + return Into5.into5().apply(fn); + } + + public static F into5(Fn5 fn, Tuple5 tuple) { + return into5(fn).apply(tuple); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into6.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into6.java new file mode 100644 index 000000000..9f9152574 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into6.java @@ -0,0 +1,42 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import com.jnape.palatable.lambda.adt.hlist.Tuple6; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.Fn2; +import com.jnape.palatable.lambda.functions.Fn6; + +/** + * Given an {@link Fn6}<A, B, C, D, E, F, G> and a + * {@link Tuple6}<A, B, C, D, E, F>, destructure the tuple and apply the slots as arguments to the + * function, returning the result. + * + * @param the first argument type + * @param the second argument type + * @param the third argument type + * @param the fourth argument type + * @param the fifth argument type + * @param the sixth argument type + * @param the result type + */ +public final class Into6 implements Fn2, Tuple6, G> { + + private static final Into6 INSTANCE = new Into6(); + + @Override + public G apply(Fn6 fn, Tuple6 tuple) { + return tuple.into(fn); + } + + @SuppressWarnings("unchecked") + public static Into6 into6() { + return INSTANCE; + } + + public static Fn1, G> into6(Fn6 fn) { + return Into6.into6().apply(fn); + } + + public static G into6(Fn6 fn, Tuple6 tuple) { + return into6(fn).apply(tuple); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into7.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into7.java new file mode 100644 index 000000000..b2a527861 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into7.java @@ -0,0 +1,43 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import com.jnape.palatable.lambda.adt.hlist.Tuple7; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.Fn2; +import com.jnape.palatable.lambda.functions.Fn7; + +/** + * Given an {@link Fn7}<A, B, C, D, E, F, G, H> and a + * {@link Tuple7}<A, B, C, D, E, F, G>, destructure the tuple and apply the slots as arguments to the + * function, returning the result. + * + * @param the first argument type + * @param the second argument type + * @param the third argument type + * @param the fourth argument type + * @param the fifth argument type + * @param the sixth argument type + * @param the seventh argument type + * @param the result type + */ +public final class Into7 implements Fn2, Tuple7, H> { + + private static final Into7 INSTANCE = new Into7(); + + @Override + public H apply(Fn7 fn, Tuple7 tuple) { + return tuple.into(fn); + } + + @SuppressWarnings("unchecked") + public static Into7 into7() { + return INSTANCE; + } + + public static Fn1, H> into7(Fn7 fn) { + return Into7.into7().apply(fn); + } + + public static H into7(Fn7 fn, Tuple7 tuple) { + return into7(fn).apply(tuple); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into8.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into8.java new file mode 100644 index 000000000..a454c0bcf --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into8.java @@ -0,0 +1,46 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import com.jnape.palatable.lambda.adt.hlist.Tuple8; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.Fn2; +import com.jnape.palatable.lambda.functions.Fn8; + +/** + * Given an {@link Fn8}<A, B, C, D, E, F, G, H, I> and a + * {@link Tuple8}<A, B, C, D, E, F, G, H>, destructure the tuple and apply the slots as arguments to + * the function, returning the result. + * + * @param the first argument type + * @param the second argument type + * @param the third argument type + * @param the fourth argument type + * @param the fifth argument type + * @param the sixth argument type + * @param the seventh argument type + * @param the eighth argument type + * @param the result type + */ +public final class Into8 implements Fn2, Tuple8, I> { + + private static final Into8 INSTANCE = new Into8(); + + @Override + public I apply(Fn8 fn, Tuple8 tuple) { + return tuple.into(fn); + } + + @SuppressWarnings("unchecked") + public static Into8 into8() { + return INSTANCE; + } + + public static Fn1, I> into8( + Fn8 fn) { + return Into8.into8().apply(fn); + } + + public static I into8(Fn8 fn, + Tuple8 tuple) { + return into8(fn).apply(tuple); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into3Test.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into3Test.java new file mode 100644 index 000000000..a8255c55e --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into3Test.java @@ -0,0 +1,15 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Into3.into3; +import static org.junit.Assert.assertEquals; + +public class Into3Test { + + @Test + public void appliesTupleToFunction() { + assertEquals((Integer) 6, into3((a, b, c) -> a + b + c, tuple(1, 2, 3))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into4Test.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into4Test.java new file mode 100644 index 000000000..7572a0c24 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into4Test.java @@ -0,0 +1,15 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Into4.into4; +import static org.junit.Assert.assertEquals; + +public class Into4Test { + + @Test + public void appliesTupleToFunction() { + assertEquals((Integer) 10, into4((a, b, c, d) -> a + b + c + d, tuple(1, 2, 3, 4))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into5Test.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into5Test.java new file mode 100644 index 000000000..6fa237738 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into5Test.java @@ -0,0 +1,15 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Into5.into5; +import static org.junit.Assert.assertEquals; + +public class Into5Test { + + @Test + public void appliesTupleToFunction() { + assertEquals((Integer) 15, into5((a, b, c, d, e) -> a + b + c + d + e, tuple(1, 2, 3, 4, 5))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into6Test.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into6Test.java new file mode 100644 index 000000000..b18abc4c0 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into6Test.java @@ -0,0 +1,15 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Into6.into6; +import static org.junit.Assert.assertEquals; + +public class Into6Test { + + @Test + public void appliesTupleToFunction() { + assertEquals((Integer) 21, into6((a, b, c, d, e, f) -> a + b + c + d + e + f, tuple(1, 2, 3, 4, 5, 6))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into7Test.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into7Test.java new file mode 100644 index 000000000..170676552 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into7Test.java @@ -0,0 +1,15 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Into7.into7; +import static org.junit.Assert.assertEquals; + +public class Into7Test { + + @Test + public void appliesTupleToFunction() { + assertEquals((Integer) 28, into7((a, b, c, d, e, f, g) -> a + b + c + d + e + f + g, tuple(1, 2, 3, 4, 5, 6, 7))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into8Test.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into8Test.java new file mode 100644 index 000000000..0c5efc698 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into8Test.java @@ -0,0 +1,15 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Into8.into8; +import static org.junit.Assert.assertEquals; + +public class Into8Test { + + @Test + public void appliesTupleToFunction() { + assertEquals((Integer) 36, into8((a, b, c, d, e, f, g, h) -> a + b + c + d + e + f + g + h, tuple(1, 2, 3, 4, 5, 6, 7, 8))); + } +} \ No newline at end of file From 979a155a7a2877bf93e74e269272c9c08495138b Mon Sep 17 00:00:00 2001 From: jnape Date: Wed, 29 Nov 2017 17:54:55 -0600 Subject: [PATCH 10/24] DroppingIterator now deforests; it's clear a general solution is needed --- .../lambda/iterators/DroppingIterator.java | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/iterators/DroppingIterator.java b/src/main/java/com/jnape/palatable/lambda/iterators/DroppingIterator.java index 9beeead50..48938ff67 100644 --- a/src/main/java/com/jnape/palatable/lambda/iterators/DroppingIterator.java +++ b/src/main/java/com/jnape/palatable/lambda/iterators/DroppingIterator.java @@ -4,9 +4,9 @@ import java.util.NoSuchElementException; public class DroppingIterator extends ImmutableIterator { - private final Integer n; - private final Iterator asIterator; - private boolean dropped; + private Integer n; + private Iterator asIterator; + private boolean dropped; public DroppingIterator(Integer n, Iterator asIterator) { this.n = n; @@ -16,7 +16,11 @@ public DroppingIterator(Integer n, Iterator asIterator) { @Override public boolean hasNext() { - dropIfNecessary(); + if (!dropped) { + deforest(); + drop(); + dropped = true; + } return asIterator.hasNext(); } @@ -28,13 +32,16 @@ public A next() { return asIterator.next(); } - private void dropIfNecessary() { - if (!dropped) { - int i = 0; - while (i++ < n && asIterator.hasNext()) - asIterator.next(); - dropped = true; + private void deforest() { + while (asIterator instanceof DroppingIterator) { + n += ((DroppingIterator) this.asIterator).n; + asIterator = ((DroppingIterator) this.asIterator).asIterator; } } + private void drop() { + int i = 0; + while (i++ < n && asIterator.hasNext()) + asIterator.next(); + } } From b890024122b1fde7552ab8ab6b0251829701e8b4 Mon Sep 17 00:00:00 2001 From: jnape Date: Thu, 30 Nov 2017 21:00:44 -0600 Subject: [PATCH 11/24] Dramatically improving the performance of Tails by avoiding nested drops --- .../lambda/functions/builtin/fn1/Tails.java | 13 ++++--------- .../lambda/functions/builtin/fn1/TailsTest.java | 10 ++++++++++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Tails.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Tails.java index ca61cec74..cbdc65589 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Tails.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Tails.java @@ -2,15 +2,13 @@ import com.jnape.palatable.lambda.functions.Fn1; -import java.util.Collections; - import static com.jnape.palatable.lambda.adt.Maybe.just; -import static com.jnape.palatable.lambda.adt.Maybe.nothing; import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; -import static com.jnape.palatable.lambda.functions.builtin.fn1.Empty.empty; -import static com.jnape.palatable.lambda.functions.builtin.fn1.Tail.tail; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Drop.drop; import static com.jnape.palatable.lambda.functions.builtin.fn2.Snoc.snoc; import static com.jnape.palatable.lambda.functions.builtin.fn2.Unfoldr.unfoldr; +import static com.jnape.palatable.lambda.functions.builtin.fn3.ZipWith.zipWith; +import static java.util.Collections.emptyList; /** * Given an {@link Iterable}<A>, produce an @@ -31,10 +29,7 @@ private Tails() { @Override public Iterable> apply(Iterable as) { - return snoc(Collections::emptyIterator, - unfoldr(next -> empty(next) - ? nothing() - : just(tuple(next, tail(next))), as)); + return snoc(emptyList(), zipWith((a, __) -> a, unfoldr(k -> just(tuple(drop(k, as), k + 1)), 0), as)); } @SuppressWarnings("unchecked") diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/TailsTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/TailsTest.java index 55e0d09fa..15960accf 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/TailsTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/TailsTest.java @@ -11,10 +11,15 @@ import testsupport.traits.InfiniteIterableSupport; import testsupport.traits.Laziness; +import static com.jnape.palatable.lambda.adt.Maybe.just; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Last.last; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat; import static com.jnape.palatable.lambda.functions.builtin.fn1.Tails.tails; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Take.take; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static testsupport.matchers.IterableMatcher.iterates; @@ -41,4 +46,9 @@ public void nonEmpty() { singletonList(5), emptyList())); } + + @Test + public void largeNumberOfElements() { + assertEquals(just(emptyList()), last(tails(take(100000, repeat(1))))); + } } \ No newline at end of file From 94ce299f5043d2889fc241e6a20bb61217d5b0c7 Mon Sep 17 00:00:00 2001 From: jnape Date: Sat, 2 Dec 2017 17:44:42 -0600 Subject: [PATCH 12/24] Adding times, for iteratively applying a function to a value n times --- CHANGELOG.md | 1 + .../lambda/functions/builtin/fn3/Times.java | 54 +++++++++++++++++++ .../functions/builtin/fn3/TimesTest.java | 41 ++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/Times.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/builtin/fn3/TimesTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 314f17ff6..6c4df4800 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `CoProduct6` through `CoProduct8` and `Choice6` through `Choice8` - `CoProduct5#diverge` and `Choice5#diverge` - `Into3` through `Into8`, for applying a `Tuple*` to an `Fn*` +- `Times`, for successively accumulating a result by iterating a function over a value some number of times ### Removed - `Either#toOptional`, deprecated in previous release diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/Times.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/Times.java new file mode 100644 index 000000000..2227c584e --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/Times.java @@ -0,0 +1,54 @@ +package com.jnape.palatable.lambda.functions.builtin.fn3; + +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.Fn2; +import com.jnape.palatable.lambda.functions.Fn3; + +import java.util.function.Function; + +import static com.jnape.palatable.lambda.functions.builtin.fn2.Replicate.replicate; +import static com.jnape.palatable.lambda.functions.builtin.fn3.FoldLeft.foldLeft; + +/** + * Given some number of times n to invoke a function A -> A, and given an input + * A, iteratively apply the function to the input, and then to the result of the invocation, a total of + * n times, returning the result. + *

+ * Example: + *

+ * times(3, x -> x + 1, 0); // 3 + * + * @param the input and output type + */ +public final class Times implements Fn3, A, A> { + + private static final Times INSTANCE = new Times(); + + private Times() { + } + + @Override + public A apply(Integer n, Function fn, A a) { + if (n < 0) + throw new IllegalStateException("n must not be less than 0"); + + return foldLeft((acc, f) -> f.apply(acc), a, replicate(n, fn)); + } + + @SuppressWarnings("unchecked") + public static Times times() { + return INSTANCE; + } + + public static Fn2, A, A> times(Integer n) { + return Times.times().apply(n); + } + + public static Fn1 times(Integer n, Function fn) { + return Times.times(n).apply(fn); + } + + public static A times(Integer n, Function fn, A a) { + return Times.times(n, fn).apply(a); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn3/TimesTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn3/TimesTest.java new file mode 100644 index 000000000..a87236fc3 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn3/TimesTest.java @@ -0,0 +1,41 @@ +package com.jnape.palatable.lambda.functions.builtin.fn3; + +import com.jnape.palatable.lambda.functions.Fn1; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; +import static com.jnape.palatable.lambda.functions.builtin.fn3.Times.times; +import static org.junit.Assert.assertEquals; + +public class TimesTest { + + @Rule public ExpectedException thrown = ExpectedException.none(); + + @Test + public void accumulatesFunctionNTimes() { + Fn1 inc = x -> x + 1; + assertEquals((Integer) 3, times(3, inc).apply(0)); + } + + @Test + public void zeroIsMeansReturnTheInputBack() { + Fn1 inc = x -> x + 1; + assertEquals((Integer) 0, times(0, inc).apply(0)); + } + + @Test + public void lessThanZeroIsIllegal() { + thrown.expect(IllegalStateException.class); + thrown.expectMessage("n must not be less than 0"); + + times(-1, id(), 1); + } + + @Test + public void stackSafety() { + int stackBlowingNumber = 50000; + assertEquals((Integer) stackBlowingNumber, times(stackBlowingNumber, x -> x + 1, 0)); + } +} \ No newline at end of file From 4014b8093f8da8bd8137c45249404858f512a455 Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 3 Dec 2017 14:57:42 -0600 Subject: [PATCH 13/24] Adding slide, for sliding a window of subelements across an Iterable --- CHANGELOG.md | 1 + .../lambda/functions/builtin/fn2/Slide.java | 49 +++++++++++++++++ .../functions/builtin/fn2/SlideTest.java | 52 +++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Slide.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/SlideTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c4df4800..c936773b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `CoProduct5#diverge` and `Choice5#diverge` - `Into3` through `Into8`, for applying a `Tuple*` to an `Fn*` - `Times`, for successively accumulating a result by iterating a function over a value some number of times +- `Slide`, for "sliding" a window of some number of elements across an `Iterable` ### Removed - `Either#toOptional`, deprecated in previous release diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Slide.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Slide.java new file mode 100644 index 000000000..69e1ec16e --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Slide.java @@ -0,0 +1,49 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.Fn2; + +import static com.jnape.palatable.lambda.functions.builtin.fn1.Init.init; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Tails.tails; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Map.map; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Take.take; +import static com.jnape.palatable.lambda.functions.builtin.fn3.Times.times; + +/** + * Given an {@link Iterable}<A>, "slide" a window of k elements across the {@link + * Iterable} by one element at a time, returning an {@link Iterable}<{@link Iterable}<A>>. + *

+ * Example: + *

+ * slide(2, asList(1, 2, 3, 4, 5)); // [[1, 2], [2, 3], [3, 4], [4, 5]] + * + * @param the Iterable element type + */ +public final class Slide implements Fn2, Iterable>> { + + private static final Slide INSTANCE = new Slide<>(); + + private Slide() { + } + + @Override + public Iterable> apply(Integer k, Iterable as) { + if (k == 0) + throw new IllegalArgumentException("k must be greater than 0"); + + return times(k, init(), map(take(k), tails(as))); + } + + @SuppressWarnings("unchecked") + public static Slide slide() { + return INSTANCE; + } + + public static Fn1, Iterable>> slide(Integer k) { + return Slide.slide().apply(k); + } + + public static Iterable> slide(Integer k, Iterable as) { + return Slide.slide(k).apply(as); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/SlideTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/SlideTest.java new file mode 100644 index 000000000..6736b37ad --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/SlideTest.java @@ -0,0 +1,52 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import com.jnape.palatable.lambda.functions.Fn1; +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.ImmutableIteration; +import testsupport.traits.InfiniteIterableSupport; +import testsupport.traits.Laziness; + +import static com.jnape.palatable.lambda.functions.builtin.fn2.Drop.drop; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Iterate.iterate; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Slide.slide; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Take.take; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.junit.Assert.assertThat; +import static testsupport.matchers.IterableMatcher.isEmpty; +import static testsupport.matchers.IterableMatcher.iterates; + +@RunWith(Traits.class) +public class SlideTest { + + @TestTraits({ImmutableIteration.class, EmptyIterableSupport.class, FiniteIteration.class, InfiniteIterableSupport.class, Laziness.class}) + public Fn1, Iterable>> testSubject() { + return slide(2); + } + + @Test + public void slidesAcrossIterable() { + assertThat(slide(1, asList(1, 2, 3)), iterates(singletonList(1), singletonList(2), singletonList(3))); + assertThat(slide(2, asList(1, 2, 3)), iterates(asList(1, 2), asList(2, 3))); + assertThat(slide(3, asList(1, 2, 3)), iterates(asList(1, 2, 3))); + assertThat(slide(4, asList(1, 2, 3)), isEmpty()); + } + + @Test(expected = IllegalArgumentException.class) + public void kMustBeGreaterThan0() { + slide(0, emptyList()); + } + + @Test + public void stackSafety() { + Integer stackBlowingNumber = 50000; + Iterable> xss = slide(2, take(stackBlowingNumber, iterate(x -> x + 1, 1))); + assertThat(drop(stackBlowingNumber - 2, xss), iterates(asList(49999, 50000))); + } +} \ No newline at end of file From b598a2344d1c5228fd06754b467cea0d291c15fb Mon Sep 17 00:00:00 2001 From: jnape Date: Tue, 5 Dec 2017 20:47:24 -0600 Subject: [PATCH 14/24] Adding Either#filter overload supporting a function instead of supplier --- CHANGELOG.md | 3 ++- .../jnape/palatable/lambda/adt/Either.java | 19 ++++++++++++++++--- .../palatable/lambda/adt/EitherTest.java | 11 +++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c936773b4..3f8a6bfad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `CoProduct5#diverge` and `Choice5#diverge` - `Into3` through `Into8`, for applying a `Tuple*` to an `Fn*` - `Times`, for successively accumulating a result by iterating a function over a value some number of times -- `Slide`, for "sliding" a window of some number of elements across an `Iterable` +- `Slide`, for "sliding" a window of some number of elements across an `Iterable` +- `Either#filter` overload supporting a function from `R` to `L` in the failing predicate case ### Removed - `Either#toOptional`, deprecated in previous release 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 b305c99d7..3be69b07f 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/Either.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/Either.java @@ -79,8 +79,8 @@ public final R orThrow(Function th } /** - * If this is a right value, apply pred to it. If the result is true, return the same value; otherwise, return the - * result of leftSupplier wrapped as a left value. + * If this is a right value, apply pred to it. If the result is true, return the same + * value; otherwise, return the result of leftSupplier wrapped as a left value. *

* If this is a left value, return it. * @@ -90,7 +90,20 @@ public final R orThrow(Function th * a left */ public final Either filter(Function pred, Supplier leftSupplier) { - return flatMap(r -> pred.apply(r) ? right(r) : left(leftSupplier.get())); + return filter(pred, __ -> leftSupplier.get()); + } + + /** + * If this is a right value, apply pred to it. If the result is true, return the same + * value; otherwise, return the results of applying the right value to leftFn wrapped as a left value. + * + * @param pred the predicate to apply to a right value + * @param leftFn the function from the right value to a left value if pred fails + * @return this is a left value or a right value that pred matches; otherwise, the result of leftFn applied to the + * right value, wrapped in a left + */ + public final Either filter(Function pred, Function leftFn) { + return flatMap(r -> pred.apply(r) ? right(r) : left(leftFn.apply(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 bde70f340..407197a8d 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java @@ -89,6 +89,17 @@ public void filterLiftsRight() { assertThat(right.filter(x -> false, () -> "bar"), is(left("bar"))); } + @Test + public void filterSupportsFunctionFromRToL() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.filter(x -> true, Object::toString), is(left)); + assertThat(left.filter(x -> false, Object::toString), is(left)); + assertThat(right.filter(x -> true, Object::toString), is(right)); + assertThat(right.filter(x -> false, Object::toString), is(left("1"))); + } + @Test public void monadicFlatMapLiftsRightAndFlattensBackToEither() { Either left = left("foo"); From 7d9de4f113322efe4376f442db383a47ec17175f Mon Sep 17 00:00:00 2001 From: jnape Date: Sat, 23 Dec 2017 16:16:26 -0600 Subject: [PATCH 15/24] - all lenses that interface with mutable structures use defensive copies - unlawful lenses are explicitly documented as such - some lenses that could be made lawful via type signature changes have been made so --- CHANGELOG.md | 17 ++ .../lambda/lens/lenses/CollectionLens.java | 46 ++++++ .../lambda/lens/lenses/EitherLens.java | 4 + .../lambda/lens/lenses/HListLens.java | 2 +- .../lambda/lens/lenses/IterableLens.java | 12 +- .../lambda/lens/lenses/ListLens.java | 35 +++- .../palatable/lambda/lens/lenses/MapLens.java | 70 +++++--- .../lambda/lens/lenses/MaybeLens.java | 15 +- .../lens/lenses/CollectionLensTest.java | 50 ++---- .../lambda/lens/lenses/HListLensTest.java | 36 ++-- .../lambda/lens/lenses/IterableLensTest.java | 12 +- .../lambda/lens/lenses/ListLensTest.java | 45 ++--- .../lambda/lens/lenses/MapLensTest.java | 154 +++++++++--------- .../lambda/lens/lenses/MaybeLensTest.java | 13 +- .../testsupport/assertion/LensAssert.java | 47 ++++++ .../testsupport/matchers/LensMatcher.java | 59 +++++++ 16 files changed, 392 insertions(+), 225 deletions(-) create mode 100644 src/test/java/testsupport/assertion/LensAssert.java create mode 100644 src/test/java/testsupport/matchers/LensMatcher.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f8a6bfad..1e19c491b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] +### Changed +- ***Breaking Change***: `CollectionLens#asSet` is now lawful and preserves new incoming values in the update set +- ***Breaking Change***: `IterableLens#head` is now a `Lens.Simple, Maybe>` and is lawful +- ***Breaking Change***: `ListLens#elementAt` is now a `Lens.Simple, Maybe>` supporting defensive copies +- ***Breaking Change***: `MapLens#valueAt` is now a `Lens.Simple, Maybe>` supporting defensive copies +- `MapLens#keys` now uses defensive copies and does not alter the focused on map +- `MapLens#values` now uses defensive copies and does not alter the focused on map +- `MapLens#inverted` now uses defensive copies and does not alter the focused on map +- `HListLens#head` is now covariant in the tail of both `S` and `T` + ### Added - `Fn3#fn3` and `Fn4#fn4` static factory methods - `Fn5` through `Fn8` @@ -15,6 +25,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `Times`, for successively accumulating a result by iterating a function over a value some number of times - `Slide`, for "sliding" a window of some number of elements across an `Iterable` - `Either#filter` overload supporting a function from `R` to `L` in the failing predicate case +- `CollectionLens#asSet(Function)`, a proper analog of `CollectionLens#asSet()` that uses defensive copies +- `CollectionLens#asStream(Function)`, a proper analog of `CollectionLens#asStream()` that uses defensive copies +- Explicitly calling attention to all unlawful lenses in their documentation ### Removed - `Either#toOptional`, deprecated in previous release @@ -24,6 +37,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `TraversableIterable`, deprecated in previous release - `Traversables`, deprecated in previous release +### Deprecated +- `CollectionLens#asSet()` in favor of `CollectionLens#asSet(Function)` +- `CollectionLens#asStream()` in favor of `CollectionLens#asStream(Function)` + ## [2.0.0] - 2017-11-13 ### Changed - ***Breaking Change***: `java.util.Optional` replaced with `Maybe` across the board diff --git a/src/main/java/com/jnape/palatable/lambda/lens/lenses/CollectionLens.java b/src/main/java/com/jnape/palatable/lambda/lens/lenses/CollectionLens.java index 5bcf16315..70af23f35 100644 --- a/src/main/java/com/jnape/palatable/lambda/lens/lenses/CollectionLens.java +++ b/src/main/java/com/jnape/palatable/lambda/lens/lenses/CollectionLens.java @@ -38,7 +38,9 @@ public static > Lens.Simple asCopy(Function< * @param the collection element type * @param the type of the collection * @return a lens that focuses on a Collection as a Set + * @deprecated in favor of lawful {@link CollectionLens#asSet(Function)} */ + @Deprecated public static > Lens.Simple> asSet() { return simpleLens(HashSet::new, (xsL, xsS) -> { xsL.retainAll(xsS); @@ -46,13 +48,36 @@ public static > Lens.Simple> asSet() { }); } + /** + * Convenience static factory method for creating a lens that focuses on an arbitrary {@link Collection} as a + * {@link Set}. + * + * @param copyFn the copying function + * @param the collection element type + * @param the type of the collection + * @return a lens that focuses on a Collection as a Set + */ + public static > Lens.Simple> asSet( + Function copyFn) { + return simpleLens(HashSet::new, (xsL, xsS) -> { + Set missing = new HashSet<>(xsS); + missing.removeAll(xsL); + CX updated = copyFn.apply(xsL); + updated.addAll(missing); + updated.retainAll(xsS); + return updated; + }); + } + /** * Convenience static factory method for creating a lens that focuses on a Collection as a Stream. * * @param the collection element type * @param the type of the collection * @return a lens that focuses on a Collection as a stream. + * @deprecated in favor of lawful {@link CollectionLens#asStream(Function)} */ + @Deprecated public static > Lens.Simple> asStream() { return simpleLens(Collection::stream, (xsL, xsS) -> { xsL.clear(); @@ -60,4 +85,25 @@ public static > Lens.Simple> asStream( return xsL; }); } + + /** + * Convenience static factory method for creating a lens that focuses on a Collection as a Stream. + *

+ * Note that this lens is effectively lawful, though difficult to prove given the intrinsically + * stateful and inequitable nature of {@link Stream}. + * + * @param copyFn the copying function + * @param the collection element type + * @param the type of the collection + * @return a lens that focuses on a Collection as a stream. + */ + public static > Lens.Simple> asStream( + Function copyFn) { + return simpleLens(Collection::stream, (xsL, xsS) -> { + CX updated = copyFn.apply(xsL); + updated.clear(); + xsS.forEach(updated::add); + return updated; + }); + } } diff --git a/src/main/java/com/jnape/palatable/lambda/lens/lenses/EitherLens.java b/src/main/java/com/jnape/palatable/lambda/lens/lenses/EitherLens.java index 4a05fb543..50b840e5c 100644 --- a/src/main/java/com/jnape/palatable/lambda/lens/lenses/EitherLens.java +++ b/src/main/java/com/jnape/palatable/lambda/lens/lenses/EitherLens.java @@ -19,6 +19,8 @@ private EitherLens() { * Convenience static factory method for creating a lens over right values, wrapping them in a {@link Maybe}. When * setting, a {@link Maybe#nothing()} value means to leave the {@link Either} unaltered, where as a * {@link Maybe#just} value replaces the either with a right over the {@link Maybe}. + *

+ * Note that this lens is NOT lawful, since "you get back what you put in" fails for {@link Maybe#nothing()}. * * @param the left parameter type * @param the right parameter type @@ -32,6 +34,8 @@ public static Lens.Simple, Maybe> right() { * Convenience static factory method for creating a lens over left values, wrapping them in a {@link Maybe}. When * setting, a {@link Maybe#nothing()} value means to leave the {@link Either} unaltered, where as a * {@link Maybe#just} value replaces the either with a left over the {@link Maybe}. + *

+ * Note that this lens is NOT lawful, since "you get back what you put in" fails for {@link Maybe#nothing()}. * * @param the left parameter type * @param the right parameter type diff --git a/src/main/java/com/jnape/palatable/lambda/lens/lenses/HListLens.java b/src/main/java/com/jnape/palatable/lambda/lens/lenses/HListLens.java index e5b7025a5..085baeaf2 100644 --- a/src/main/java/com/jnape/palatable/lambda/lens/lenses/HListLens.java +++ b/src/main/java/com/jnape/palatable/lambda/lens/lenses/HListLens.java @@ -32,7 +32,7 @@ public final class HListLens { * @param the tail HList type * @return a lens that focuses on the head of an HList */ - public static Lens.Simple, Head> head() { + public static Lens.Simple, Head> head() { return simpleLens(HCons::head, (hCons, newHead) -> cons(newHead, hCons.tail())); } diff --git a/src/main/java/com/jnape/palatable/lambda/lens/lenses/IterableLens.java b/src/main/java/com/jnape/palatable/lambda/lens/lenses/IterableLens.java index 26e6c2ffb..0c765505d 100644 --- a/src/main/java/com/jnape/palatable/lambda/lens/lenses/IterableLens.java +++ b/src/main/java/com/jnape/palatable/lambda/lens/lenses/IterableLens.java @@ -3,13 +3,11 @@ import com.jnape.palatable.lambda.adt.Maybe; import com.jnape.palatable.lambda.functions.builtin.fn1.Head; import com.jnape.palatable.lambda.functions.builtin.fn1.Tail; -import com.jnape.palatable.lambda.functions.builtin.fn2.Cons; import com.jnape.palatable.lambda.lens.Lens; import static com.jnape.palatable.lambda.functions.Fn2.fn2; import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; import static com.jnape.palatable.lambda.functions.builtin.fn2.Cons.cons; -import static com.jnape.palatable.lambda.lens.Lens.lens; import static com.jnape.palatable.lambda.lens.Lens.simpleLens; /** @@ -22,12 +20,18 @@ private IterableLens() { /** * A lens focusing on the head of a given {@link Iterable}. + *

+ * Note that this lens is effectively lawful, though difficult to prove since there is no + * useful equality implementation for {@link Iterable}. * * @param the Iterable element type * @return a lens focusing on the head element of an {@link Iterable} */ - public static Lens, Iterable, Maybe, A> head() { - return lens(Head::head, Cons.cons().flip().compose(Tail.tail()).toBiFunction()); + public static Lens.Simple, Maybe> head() { + return simpleLens(Head::head, (s, maybeB) -> { + Iterable tail = Tail.tail(s); + return maybeB.fmap(b -> cons(b, tail)).orElse(tail); + }); } /** diff --git a/src/main/java/com/jnape/palatable/lambda/lens/lenses/ListLens.java b/src/main/java/com/jnape/palatable/lambda/lens/lenses/ListLens.java index c0f9939e9..b7857df2a 100644 --- a/src/main/java/com/jnape/palatable/lambda/lens/lenses/ListLens.java +++ b/src/main/java/com/jnape/palatable/lambda/lens/lenses/ListLens.java @@ -7,9 +7,12 @@ import java.util.List; import static com.jnape.palatable.lambda.adt.Maybe.maybe; -import static com.jnape.palatable.lambda.lens.Lens.lens; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Take.take; import static com.jnape.palatable.lambda.lens.Lens.simpleLens; import static com.jnape.palatable.lambda.lens.lenses.MaybeLens.unLiftA; +import static com.jnape.palatable.lambda.lens.lenses.MaybeLens.unLiftB; +import static java.lang.Math.abs; /** * Lenses that operate on {@link List}s. @@ -38,18 +41,32 @@ public static Lens.Simple, List> asCopy() { * @param the list element type * @return Maybe the element at the index */ - public static Lens, List, Maybe, X> elementAt(int index) { - return lens(xs -> maybe(xs.size() > index ? xs.get(index) : null), - (xs, x) -> { - if (xs.size() > index) - xs.set(index, x); - return xs; - }); + public static Lens.Simple, Maybe> elementAt(int index) { + return simpleLens(xs -> maybe(xs.size() > index ? xs.get(index) : null), + (xs, maybeX) -> { + List updated = new ArrayList<>(xs); + return maybeX.fmap(x -> { + int minimumSize = index + 1; + if (minimumSize > xs.size()) { + take(abs(updated.size() - minimumSize), repeat((X) null)).forEach(updated::add); + } + updated.set(index, x); + return updated; + }).orElseGet(() -> { + if (updated.size() > index) { + updated.set(index, null); + } + return updated; + }); + }); } /** * Convenience static factory method for creating a lens that focuses on an element in a list at a particular index, * returning defaultValue if there is no value at that index. + *

+ * Note that this lens is NOT lawful, since "putting back what you got changes nothing" fails for any value + * B where S is the empty list * * @param index the index to focus on * @param defaultValue the value to use if there is no element at index @@ -58,6 +75,6 @@ public static Lens, List, Maybe, X> elementAt(int index) { */ @SuppressWarnings("unchecked") public static Lens.Simple, X> elementAt(int index, X defaultValue) { - return unLiftA(elementAt(index), defaultValue)::apply; + return unLiftB(unLiftA(elementAt(index), defaultValue))::apply; } } diff --git a/src/main/java/com/jnape/palatable/lambda/lens/lenses/MapLens.java b/src/main/java/com/jnape/palatable/lambda/lens/lenses/MapLens.java index 42f0e8b7f..f45d535a4 100644 --- a/src/main/java/com/jnape/palatable/lambda/lens/lenses/MapLens.java +++ b/src/main/java/com/jnape/palatable/lambda/lens/lenses/MapLens.java @@ -5,6 +5,7 @@ import com.jnape.palatable.lambda.functions.builtin.fn2.Filter; import com.jnape.palatable.lambda.lens.Lens; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -17,11 +18,10 @@ import static com.jnape.palatable.lambda.functions.builtin.fn2.Map.map; import static com.jnape.palatable.lambda.functions.builtin.fn2.ToCollection.toCollection; import static com.jnape.palatable.lambda.functions.builtin.fn2.ToMap.toMap; -import static com.jnape.palatable.lambda.lens.Lens.lens; import static com.jnape.palatable.lambda.lens.Lens.simpleLens; import static com.jnape.palatable.lambda.lens.functions.View.view; import static com.jnape.palatable.lambda.lens.lenses.MaybeLens.unLiftA; -import static java.util.stream.Collectors.toSet; +import static com.jnape.palatable.lambda.lens.lenses.MaybeLens.unLiftB; /** * Lenses that operate on {@link Map}s. @@ -45,20 +45,29 @@ public static Lens.Simple, Map> asCopy() { /** * A lens that focuses on a value at a key in a map, as a {@link Maybe}. * - * @param k the key to focus on * @param the key type * @param the value type + * @param k the key to focus on * @return a lens that focuses on the value at key, as a {@link Maybe} */ - public static Lens, Map, Maybe, V> valueAt(K k) { - return lens(m -> maybe(m.get(k)), (m, v) -> { - m.put(k, v); - return m; + public static Lens.Simple, Maybe> valueAt(K k) { + return simpleLens(m -> maybe(m.get(k)), (m, maybeV) -> { + Map updated = new HashMap<>(m); + return maybeV.fmap(v -> { + updated.put(k, v); + return updated; + }).orElseGet(() -> { + updated.remove(k); + return updated; + }); }); } /** * A lens that focuses on a value at a key in a map, falling back to defaultV if the value is missing. + *

+ * Note that this lens is NOT lawful, since "putting back what you got changes nothing" fails for any value + * B where S is the empty map * * @param k the key to focus on * @param defaultValue the default value to use in case of a missing value at key @@ -68,7 +77,7 @@ public static Lens, Map, Maybe, V> valueAt(K k) { */ @SuppressWarnings("unchecked") public static Lens.Simple, V> valueAt(K k, V defaultValue) { - return unLiftA(valueAt(k), defaultValue)::apply; + return unLiftB(unLiftA(valueAt(k), defaultValue))::apply; } /** @@ -79,32 +88,38 @@ public static Lens.Simple, V> valueAt(K k, V defaultValue) { * @return a lens that focuses on the keys of a map */ public static Lens.Simple, Set> keys() { - return simpleLens(Map::keySet, (m, ks) -> { - Set keys = m.keySet(); - keys.retainAll(ks); - ks.removeAll(keys); - ks.forEach(k -> m.put(k, null)); - return m; + return simpleLens(m -> new HashSet<>(m.keySet()), (m, ks) -> { + HashSet ksCopy = new HashSet<>(ks); + Map updated = new HashMap<>(m); + Set keys = updated.keySet(); + keys.retainAll(ksCopy); + ksCopy.removeAll(keys); + ksCopy.forEach(k -> updated.put(k, null)); + return updated; }); } /** * A lens that focuses on the values of a map. In the case of updating the map, only the entries with a value listed * in the update collection of values are kept. + *

+ * Note that this lens is NOT lawful, since "you get back what you put in" fails for all values B that + * represent a non-surjective superset of the existing values in S. * * @param the key type * @param the value type * @return a lens that focuses on the values of a map */ public static Lens.Simple, Collection> values() { - return simpleLens(Map::values, (m, vs) -> { + return simpleLens(m -> new ArrayList<>(m.values()), (m, vs) -> { + Map updated = new HashMap<>(m); Set valueSet = new HashSet<>(vs); - Set matchingKeys = m.entrySet().stream() - .filter(kv -> valueSet.contains(kv.getValue())) - .map(Map.Entry::getKey) - .collect(toSet()); - m.keySet().retainAll(matchingKeys); - return m; + Set matchingKeys = Filter.>filter(kv -> valueSet.contains(kv.getValue())) + .andThen(map(Map.Entry::getKey)) + .andThen(toCollection(HashSet::new)) + .apply(updated.entrySet()); + updated.keySet().retainAll(matchingKeys); + return updated; }); } @@ -122,19 +137,24 @@ public static Lens.Simple, Map> inverted() { m.forEach((key, value) -> inverted.put(value, key)); return inverted; }, (m, im) -> { - m.clear(); - m.putAll(view(inverted(), im)); - return m; + Map updated = new HashMap<>(m); + updated.clear(); + updated.putAll(view(inverted(), im)); + return updated; }); } /** * A lens that focuses on a map while mapping its values with the mapping function. + *

+ * Note that this lens is NOT lawful, since "you get back what you put in" fails for all values B that + * do not map from the current values in S (new mappings cannot be preserved as the inversion of + * fn is not known). * * @param fn the mapping function * @param the key type * @param the unfocused map value type - * @param the focused map value type + * @param the focused map value types * @return a lens that focuses on a map while mapping its values */ public static Lens.Simple, Map> mappingValues(Function fn) { diff --git a/src/main/java/com/jnape/palatable/lambda/lens/lenses/MaybeLens.java b/src/main/java/com/jnape/palatable/lambda/lens/lenses/MaybeLens.java index 30888d815..0917b07ae 100644 --- a/src/main/java/com/jnape/palatable/lambda/lens/lenses/MaybeLens.java +++ b/src/main/java/com/jnape/palatable/lambda/lens/lenses/MaybeLens.java @@ -15,6 +15,9 @@ private MaybeLens() { /** * Given a lens and a default S, lift S into {@link Maybe}. + *

+ * Note that this lens is NOT lawful, since "putting back what you got changes nothing" fails for any value + * B where S is {@link Maybe#nothing()}. * * @param lens the lens * @param defaultS the S to use if {@link Maybe#nothing()} is given @@ -58,6 +61,9 @@ public static Lens, B> liftA(Lens lens) /** * Given a lens and a default B, lift B into {@link Maybe}. + *

+ * Note that this lens is NOT lawful, since "putting back what you got changes nothing" fails for any value + * B where S is {@link Maybe#nothing()}. * * @param lens the lens * @param defaultB the B to use if {@link Maybe#nothing()} is given @@ -88,6 +94,9 @@ public static Lens unLiftS(Lens, T, A, B> lens /** * Given a lens with T lifted into {@link Maybe} and a default T, flatten T * back down. + *

+ * Note that while this lens is not *necessarily* unlawful, it probably is, since the only case where "you get back + * what you put in" would not be violated is if T could never be {@link Maybe#nothing()}. * * @param lens the lens * @param defaultT the T to use if lens produces {@link Maybe#nothing()} @@ -103,8 +112,10 @@ public static Lens unLiftT(Lens, A, B> lens /** * Given a lens with A lifted into {@link Maybe} and a default A, flatten A - * back - * down. + * back down. + *

+ * Note that while this lens is not *necessarily* unlawful, it probably is, since the only case where "putting back + * what you got changes nothing" would not be violated is if A could never be {@link Maybe#nothing()}. * * @param lens the lens * @param defaultA the A to use if lens produces {@link Maybe#nothing()} diff --git a/src/test/java/com/jnape/palatable/lambda/lens/lenses/CollectionLensTest.java b/src/test/java/com/jnape/palatable/lambda/lens/lenses/CollectionLensTest.java index 2df8dd73a..07d88c2de 100644 --- a/src/test/java/com/jnape/palatable/lambda/lens/lenses/CollectionLensTest.java +++ b/src/test/java/com/jnape/palatable/lambda/lens/lenses/CollectionLensTest.java @@ -1,72 +1,46 @@ package com.jnape.palatable.lambda.lens.lenses; import com.jnape.palatable.lambda.lens.Lens; -import org.junit.Before; import org.junit.Test; import java.util.ArrayList; import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.stream.Stream; import static com.jnape.palatable.lambda.lens.functions.Set.set; import static com.jnape.palatable.lambda.lens.functions.View.view; +import static com.jnape.palatable.lambda.lens.lenses.CollectionLens.asCopy; +import static com.jnape.palatable.lambda.lens.lenses.CollectionLens.asSet; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.emptySet; import static java.util.Collections.singleton; import static java.util.stream.Collectors.toList; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; +import static testsupport.assertion.LensAssert.assertLensLawfulness; public class CollectionLensTest { - private List xs; - - @Before - public void setUp() { - xs = new ArrayList() {{ - add("foo"); - add("bar"); - add("baz"); - }}; - } - @Test public void asCopyUsesMappingFunctionToFocusOnCollectionThroughCopy() { - Lens.Simple, List> asCopy = CollectionLens.asCopy(ArrayList::new); - - assertEquals(xs, view(asCopy, xs)); - assertNotSame(xs, view(asCopy, xs)); - - List updatedList = asList("foo", "bar"); - assertSame(updatedList, set(asCopy, updatedList, xs)); + assertLensLawfulness(asCopy(ArrayList::new), + asList(emptyList(), asList("foo", "bar", "baz")), + asList(emptyList(), asList("foo", "bar", "baz"))); } @Test public void asSetFocusesOnCollectionAsSet() { - Lens.Simple, Set> asSet = CollectionLens.asSet(); - - assertEquals(new HashSet<>(xs), view(asSet, xs)); - assertEquals(singleton("foo"), view(asSet, asList("foo", "foo"))); - assertEquals(emptySet(), view(asSet, emptyList())); - - assertEquals(asList("foo", "bar"), set(asSet, new HashSet<>(asList("foo", "bar")), xs)); - assertEquals(asList("foo", "foo", "bar"), - set(asSet, - new HashSet<>(asList("foo", "bar")), - new ArrayList<>(asList("foo", "foo", "bar", "baz")))); - assertEquals(emptyList(), set(asSet, emptySet(), xs)); - assertEquals(emptyList(), set(asSet, singleton("foo"), emptyList())); + assertLensLawfulness(asSet(ArrayList::new), + asList(emptyList(), asList("foo", "bar", "baz"), asList("foo", "foo")), + asList(emptySet(), singleton("foo"), new HashSet<>(asList("foo", "bar", "baz")), new HashSet<>(asList("foo", "bar")))); } @Test public void asStreamFocusesOnCollectionAsStream() { - Lens.Simple, Stream> asStream = CollectionLens.asStream(); + Lens.Simple, Stream> asStream = CollectionLens.asStream(ArrayList::new); - assertEquals(xs, view(asStream, xs).collect(toList())); - assertEquals(asList("foo", "bar"), set(asStream, Stream.of("foo", "bar"), xs)); + assertEquals(asList("foo", "bar", "baz"), view(asStream, asList("foo", "bar", "baz")).collect(toList())); + assertEquals(asList("foo", "bar"), set(asStream, Stream.of("foo", "bar"), asList("foo", "bar", "baz"))); } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/lens/lenses/HListLensTest.java b/src/test/java/com/jnape/palatable/lambda/lens/lenses/HListLensTest.java index 58f09af70..6296ff3ef 100644 --- a/src/test/java/com/jnape/palatable/lambda/lens/lenses/HListLensTest.java +++ b/src/test/java/com/jnape/palatable/lambda/lens/lenses/HListLensTest.java @@ -1,45 +1,35 @@ package com.jnape.palatable.lambda.lens.lenses; -import com.jnape.palatable.lambda.adt.hlist.HList.HCons; import com.jnape.palatable.lambda.adt.hlist.Index; -import com.jnape.palatable.lambda.adt.hlist.Tuple2; -import com.jnape.palatable.lambda.lens.Lens; import org.junit.Test; +import static com.jnape.palatable.lambda.adt.hlist.HList.singletonHList; import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; -import static com.jnape.palatable.lambda.lens.functions.Over.over; -import static com.jnape.palatable.lambda.lens.functions.Set.set; -import static com.jnape.palatable.lambda.lens.functions.View.view; import static com.jnape.palatable.lambda.lens.lenses.HListLens.elementAt; -import static org.junit.Assert.assertEquals; +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static testsupport.assertion.LensAssert.assertLensLawfulness; public class HListLensTest { @Test public void elementAtFocusesOnInvariantElementAtIndex() { - Lens.Simple>>, String> lens = - elementAt(Index.index().after().after()); - - assertEquals("foo", view(lens, tuple(true, 0, "foo"))); - assertEquals(tuple(true, 0, "bar"), set(lens, "bar", tuple(true, 0, "foo"))); - assertEquals(tuple(true, 0, "FOO"), over(lens, String::toUpperCase, tuple(true, 0, "foo"))); + assertLensLawfulness(elementAt(Index.index().after().after()), + asList(tuple(true, 0, "foo"), tuple(true, 0, "foo", '1')), + singletonList("bar")); } @Test public void headFocusesOnHead() { - Lens.Simple>, Integer> index = HListLens.head(); - - assertEquals((Integer) 2, view(index, tuple(2, "3", '4'))); - assertEquals(tuple(0, "3", '4'), set(index, 0, tuple(2, "3", '4'))); - assertEquals(tuple(3, "3", '4'), over(index, x -> x + 1, tuple(2, "3", '4'))); + assertLensLawfulness(HListLens.head(), + asList(singletonHList(2), tuple(2, "3"), tuple(2, "3", '4')), + singletonList(0)); } @Test public void tailFocusesOnTail() { - Lens.Simple>, Tuple2> index = HListLens.tail(); - - assertEquals(tuple("3", '4'), view(index, tuple(2, "3", '4'))); - assertEquals(tuple(2, "foo", '1'), set(index, tuple("foo", '1'), tuple(2, "3", '4'))); - assertEquals(tuple(2, "FOO", 'A'), over(index, t -> t.biMap(String::toUpperCase, Character::toUpperCase), tuple(2, "foo", 'a'))); + assertLensLawfulness(HListLens.tail(), + singletonList(tuple(2, "3", '4')), + asList(tuple("3", '5'), tuple("4", '4'), tuple("4", '5'))); } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/lens/lenses/IterableLensTest.java b/src/test/java/com/jnape/palatable/lambda/lens/lenses/IterableLensTest.java index ed878eb75..ff5e30327 100644 --- a/src/test/java/com/jnape/palatable/lambda/lens/lenses/IterableLensTest.java +++ b/src/test/java/com/jnape/palatable/lambda/lens/lenses/IterableLensTest.java @@ -22,16 +22,18 @@ public class IterableLensTest { @Test public void head() { - Lens, Iterable, Maybe, Integer> head = IterableLens.head(); + Lens.Simple, Maybe> head = IterableLens.head(); assertEquals(just(1), view(head, asList(1, 2, 3))); assertEquals(nothing(), view(head, emptyList())); - assertThat(set(head, 1, emptyList()), iterates(1)); - assertThat(set(head, 1, asList(2, 2, 3)), iterates(1, 2, 3)); + assertThat(set(head, just(1), emptyList()), iterates(1)); + assertThat(set(head, nothing(), emptyList()), isEmpty()); + assertThat(set(head, just(1), asList(2, 2, 3)), iterates(1, 2, 3)); + assertThat(set(head, nothing(), asList(2, 2, 3)), iterates(2, 3)); - assertThat(over(head, x -> x.orElse(0) + 1, emptyList()), iterates(1)); - assertThat(over(head, x -> x.orElse(0) + 1, asList(1, 2, 3)), iterates(2, 2, 3)); + assertThat(over(head, maybeX -> maybeX.fmap(x -> x + 1), emptyList()), isEmpty()); + assertThat(over(head, maybeX -> maybeX.fmap(x -> x + 1), asList(1, 2, 3)), iterates(2, 2, 3)); } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/lens/lenses/ListLensTest.java b/src/test/java/com/jnape/palatable/lambda/lens/lenses/ListLensTest.java index a405dc33f..78949c068 100644 --- a/src/test/java/com/jnape/palatable/lambda/lens/lenses/ListLensTest.java +++ b/src/test/java/com/jnape/palatable/lambda/lens/lenses/ListLensTest.java @@ -1,64 +1,45 @@ package com.jnape.palatable.lambda.lens.lenses; -import com.jnape.palatable.lambda.adt.Maybe; import com.jnape.palatable.lambda.lens.Lens; -import org.junit.Before; import org.junit.Test; -import java.util.ArrayList; import java.util.List; import static com.jnape.palatable.lambda.adt.Maybe.just; import static com.jnape.palatable.lambda.adt.Maybe.nothing; import static com.jnape.palatable.lambda.lens.functions.Set.set; import static com.jnape.palatable.lambda.lens.functions.View.view; +import static com.jnape.palatable.lambda.lens.lenses.ListLens.asCopy; +import static com.jnape.palatable.lambda.lens.lenses.ListLens.elementAt; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; +import static testsupport.assertion.LensAssert.assertLensLawfulness; public class ListLensTest { - private List xs; - - @Before - public void setUp() throws Exception { - xs = new ArrayList() {{ - add("foo"); - add("bar"); - add("baz"); - }}; - } - @Test public void asCopyFocusesOnListThroughCopy() { - Lens.Simple, List> asCopy = ListLens.asCopy(); - - assertEquals(xs, view(asCopy, xs)); - assertNotSame(xs, view(asCopy, xs)); - - List update = asList("foo", "bar", "baz", "quux"); - assertSame(update, set(asCopy, update, xs)); + assertLensLawfulness(asCopy(), + asList(emptyList(), asList("foo", "bar", "baz")), + asList(emptyList(), asList("foo", "bar", "baz", "quux"))); } @Test public void elementAtFocusesOnElementAtIndex() { - Lens, List, Maybe, String> at0 = ListLens.elementAt(0); - - assertEquals(just("foo"), view(at0, xs)); - assertEquals(nothing(), view(at0, emptyList())); - assertEquals(asList("quux", "bar", "baz"), set(at0, "quux", xs)); - assertEquals(emptyList(), set(at0, "quux", emptyList())); + assertLensLawfulness(elementAt(0), + asList(emptyList(), asList("foo", "bar", "baz"), asList("quux", "bar", "baz")), + asList(nothing(), just("foo"), just("quux"))); } @Test public void elementAtWithDefaultValueFocusesOnElementAtIndex() { Lens, List, String, String> at0 = ListLens.elementAt(0, "missing"); - assertEquals("foo", view(at0, xs)); + assertEquals("foo", view(at0, asList("foo", "bar", "baz"))); assertEquals("missing", view(at0, emptyList())); - assertEquals(asList("quux", "bar", "baz"), set(at0, "quux", xs)); - assertEquals(emptyList(), set(at0, "quux", emptyList())); + assertEquals(asList("quux", "bar", "baz"), set(at0, "quux", asList("foo", "bar", "baz"))); + assertEquals(singletonList("quux"), set(at0, "quux", emptyList())); } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/lens/lenses/MapLensTest.java b/src/test/java/com/jnape/palatable/lambda/lens/lenses/MapLensTest.java index ebd19025a..aceb8c626 100644 --- a/src/test/java/com/jnape/palatable/lambda/lens/lenses/MapLensTest.java +++ b/src/test/java/com/jnape/palatable/lambda/lens/lenses/MapLensTest.java @@ -1,142 +1,138 @@ package com.jnape.palatable.lambda.lens.lenses; -import com.jnape.palatable.lambda.adt.Maybe; import com.jnape.palatable.lambda.lens.Lens; -import org.junit.Before; import org.junit.Test; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; -import java.util.Set; import static com.jnape.palatable.lambda.adt.Maybe.just; +import static com.jnape.palatable.lambda.adt.Maybe.nothing; import static com.jnape.palatable.lambda.lens.functions.Set.set; import static com.jnape.palatable.lambda.lens.functions.View.view; import static com.jnape.palatable.lambda.lens.lenses.MapLens.keys; import static com.jnape.palatable.lambda.lens.lenses.MapLens.mappingValues; +import static com.jnape.palatable.lambda.lens.lenses.MapLens.valueAt; import static java.util.Arrays.asList; import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; +import static java.util.Collections.singleton; import static java.util.Collections.singletonMap; import static java.util.Collections.unmodifiableMap; +import static org.hamcrest.core.IsCollectionContaining.hasItems; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; +import static testsupport.assertion.LensAssert.assertLensLawfulness; public class MapLensTest { - private Map m; - - @Before - public void setUp() { - m = new HashMap() {{ - put("foo", 1); - put("bar", 2); - put("baz", 3); - }}; - } - @Test public void asCopyFocusesOnMapThroughCopy() { - Lens.Simple, Map> asCopy = MapLens.asCopy(); - - assertEquals(m, view(asCopy, m)); - assertNotSame(m, view(asCopy, m)); - - Map update = new HashMap() {{ - put("quux", 0); - }}; - assertSame(update, set(asCopy, update, m)); + assertLensLawfulness(MapLens.asCopy(), + asList(emptyMap(), singletonMap("foo", 1), new HashMap() {{ + put("foo", 1); + put("bar", 2); + put("baz", 3); + }}), + asList(emptyMap(), singletonMap("foo", 1), new HashMap() {{ + put("foo", 1); + put("bar", 2); + put("baz", 3); + }})); } @Test public void valueAtFocusesOnValueAtKey() { - Lens, Map, Maybe, Integer> atFoo = MapLens.valueAt("foo"); - - assertEquals(just(1), view(atFoo, m)); - - Map updated = set(atFoo, -1, m); - assertEquals(new HashMap() {{ - put("foo", -1); - put("bar", 2); - put("baz", 3); - }}, updated); - assertSame(m, updated); + assertLensLawfulness(valueAt("foo"), + asList(emptyMap(), singletonMap("foo", 1), new HashMap() {{ + put("foo", 1); + put("bar", 2); + put("baz", 3); + }}), + asList(nothing(), just(1))); } @Test public void valueAtWithDefaultValueFocusedOnValueAtKey() { - Lens, Map, Integer, Integer> atFoo = MapLens.valueAt("foo", -1); + Lens.Simple, Integer> atFoo = valueAt("foo", -1); - assertEquals((Integer) 1, view(atFoo, m)); + assertEquals((Integer) 1, view(atFoo, new HashMap() {{ + put("foo", 1); + put("bar", 2); + put("baz", 3); + }})); assertEquals((Integer) (-1), view(atFoo, emptyMap())); - Map updated = set(atFoo, 11, m); + Map updated = set(atFoo, 11, new HashMap() {{ + put("foo", 1); + put("bar", 2); + put("baz", 3); + }}); assertEquals(new HashMap() {{ put("foo", 11); put("bar", 2); put("baz", 3); }}, updated); - assertSame(m, updated); + assertNotSame(new HashMap() {{ + put("foo", 1); + put("bar", 2); + put("baz", 3); + }}, updated); } @Test public void keysFocusesOnKeys() { - Lens, Map, Set, Set> keys = keys(); - - assertEquals(m.keySet(), view(keys, m)); - - Map updated = set(keys, new HashSet<>(asList("bar", "baz", "quux")), m); - assertEquals(new HashMap() {{ - put("bar", 2); - put("baz", 3); - put("quux", null); - }}, updated); - assertSame(m, updated); + assertLensLawfulness(keys(), + asList(emptyMap(), singletonMap("foo", 1), new HashMap() {{ + put("foo", 1); + put("bar", 2); + put("baz", 3); + }}), + asList(emptySet(), singleton("foo"), new HashSet<>(asList("foo", "bar", "baz", "quux")), new HashSet<>(asList("foo", "baz", "quux")))); } @Test public void valuesFocusesOnValues() { - Lens, Map, Collection, Collection> values = MapLens.values(); + Lens.Simple, Collection> values = MapLens.values(); - assertEquals(m.values(), view(values, m)); + assertThat(view(values, new HashMap() {{ + put("foo", 1); + put("bar", 2); + put("baz", 3); + }}), hasItems(2, 1, 3)); - Map updated = set(values, asList(1, 2), m); + Map updated = set(values, asList(1, 2), new HashMap() {{ + put("foo", 1); + put("bar", 2); + put("baz", 3); + }}); assertEquals(new HashMap() {{ put("foo", 1); put("bar", 2); }}, updated); - assertSame(m, updated); - } - - @Test - public void invertedFocusesOnMapWithKeysAndValuesSwitched() { - Lens.Simple, Map> inverted = MapLens.inverted(); - - assertEquals(new HashMap() {{ - put(1, "foo"); - put(2, "bar"); - put(3, "baz"); - }}, view(inverted, m)); - - Map updated = set(inverted, new HashMap() {{ - put(2, "bar"); - put(3, "baz"); - }}, m); - assertEquals(new HashMap() {{ + assertNotSame(new HashMap() {{ + put("foo", 1); put("bar", 2); put("baz", 3); }}, updated); - assertSame(m, updated); + } - Map withDuplicateValues = new HashMap() {{ - put("foo", 1); - put("bar", 1); - }}; - assertEquals(new HashMap() {{ - put(1, "foo"); - }}, view(inverted, withDuplicateValues)); + @Test + public void invertedFocusesOnMapWithKeysAndValuesSwitched() { + assertLensLawfulness(MapLens.inverted(), + asList(emptyMap(), singletonMap("foo", 1), new HashMap() {{ + put("foo", 1); + put("bar", 2); + put("baz", 3); + }}), + asList(emptyMap(), singletonMap(1, "foo"), new HashMap() {{ + put(1, "foo"); + put(2, "bar"); + put(3, "baz"); + }})); } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/lens/lenses/MaybeLensTest.java b/src/test/java/com/jnape/palatable/lambda/lens/lenses/MaybeLensTest.java index 4af6e6777..445aa8a73 100644 --- a/src/test/java/com/jnape/palatable/lambda/lens/lenses/MaybeLensTest.java +++ b/src/test/java/com/jnape/palatable/lambda/lens/lenses/MaybeLensTest.java @@ -14,25 +14,24 @@ import static com.jnape.palatable.lambda.lens.lenses.MaybeLens.liftB; import static com.jnape.palatable.lambda.lens.lenses.MaybeLens.liftS; import static com.jnape.palatable.lambda.lens.lenses.MaybeLens.liftT; +import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; +import static testsupport.assertion.LensAssert.assertLensLawfulness; public class MaybeLensTest { private Lens lens; @Before - public void setUp() throws Exception { + public void setUp() { lens = lens(s -> s.charAt(0), (s, b) -> s.length() == b); } @Test public void asMaybeWrapsValuesInMaybe() { - Lens.Simple> asMaybe = MaybeLens.asMaybe(); - - assertEquals(just("foo"), view(asMaybe, "foo")); - assertEquals(nothing(), view(asMaybe, null)); - assertEquals("bar", set(asMaybe, just("bar"), "foo")); - assertEquals("foo", set(asMaybe, nothing(), "foo")); + assertLensLawfulness(MaybeLens.asMaybe(), + asList(null, "foo"), + asList(nothing(), just("hi"))); } @Test diff --git a/src/test/java/testsupport/assertion/LensAssert.java b/src/test/java/testsupport/assertion/LensAssert.java new file mode 100644 index 000000000..3f2e80ae8 --- /dev/null +++ b/src/test/java/testsupport/assertion/LensAssert.java @@ -0,0 +1,47 @@ +package testsupport.assertion; + +import com.jnape.palatable.lambda.adt.Maybe; +import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import com.jnape.palatable.lambda.functions.Fn2; +import com.jnape.palatable.lambda.functions.builtin.fn2.Map; +import com.jnape.palatable.lambda.lens.Lens; +import com.jnape.palatable.lambda.monoid.builtin.Present; + +import java.util.Objects; + +import static com.jnape.palatable.lambda.adt.Maybe.just; +import static com.jnape.palatable.lambda.adt.Maybe.nothing; +import static com.jnape.palatable.lambda.functions.builtin.fn1.CatMaybes.catMaybes; +import static com.jnape.palatable.lambda.functions.builtin.fn2.CartesianProduct.cartesianProduct; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Into.into; +import static com.jnape.palatable.lambda.functions.builtin.fn2.ReduceLeft.reduceLeft; +import static com.jnape.palatable.lambda.lens.functions.Set.set; +import static com.jnape.palatable.lambda.lens.functions.View.view; +import static java.lang.String.format; +import static java.lang.String.join; +import static java.util.Arrays.asList; + +public final class LensAssert { + + public static void assertLensLawfulness(Lens lens, Iterable ss, Iterable bs) { + Iterable> cases = cartesianProduct(ss, bs); + Present.present((x, y) -> join("\n\n", x, y)) + .reduceLeft(asList(falsify("You get back what you put in", (s, b) -> view(lens, set(lens, b, s)), (s, b) -> b, cases), + falsify("Putting back what you got changes nothing", (s, b) -> set(lens, view(lens, s), s), (s, b) -> s, cases), + falsify("Setting twice is equivalent to setting once", (s, b) -> set(lens, b, set(lens, b, s)), (s, b) -> set(lens, b, s), cases))) + .peek(failures -> {throw new AssertionError("Lens law failures\n\n" + failures);}); + } + + private static Maybe falsify(String label, Fn2 l, Fn2 r, + Iterable> cases) { + return Map., Maybe>map(into((s, b) -> { + X x = l.apply(s, b); + X y = r.apply(s, b); + return Objects.equals(x, y) ? nothing() : just(format("S <%s>, B <%s> (%s != %s)", s, b, x, y)); + })) + .fmap(catMaybes()) + .fmap(reduceLeft((x, y) -> x + "\n\t - " + y)) + .fmap(maybeFailures -> maybeFailures.fmap(failures -> "\"" + label + "\" failed for the following cases:\n\n\t - " + failures)) + .apply(cases); + } +} diff --git a/src/test/java/testsupport/matchers/LensMatcher.java b/src/test/java/testsupport/matchers/LensMatcher.java new file mode 100644 index 000000000..c25f1fa86 --- /dev/null +++ b/src/test/java/testsupport/matchers/LensMatcher.java @@ -0,0 +1,59 @@ +package testsupport.matchers; + +import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import com.jnape.palatable.lambda.lens.Lens; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; + +import java.util.HashSet; + +import static com.jnape.palatable.lambda.functions.builtin.fn1.Empty.empty; +import static com.jnape.palatable.lambda.functions.builtin.fn2.All.all; +import static com.jnape.palatable.lambda.functions.builtin.fn2.CartesianProduct.cartesianProduct; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Filter.filter; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Into.into; +import static com.jnape.palatable.lambda.functions.builtin.fn2.ToCollection.toCollection; +import static com.jnape.palatable.lambda.lens.functions.Set.set; +import static com.jnape.palatable.lambda.lens.functions.View.view; + +public class LensMatcher extends BaseMatcher> { + + private final Iterable> combinations; + + private LensMatcher(Iterable> combinations) { + this.combinations = combinations; + } + + @Override + @SuppressWarnings("unchecked") + public boolean matches(Object other) { + if (!(other instanceof Lens)) + return false; + + Lens lens = (Lens) other; + return youGetBackWhatYouPutIn(lens) + && puttingBackWhatYouGotChangesNothing(lens) + && settingTwiceIsEquivalentToSettingOnce(lens); + } + + @Override + public void describeTo(Description description) { + throw new UnsupportedOperationException(); + } + + private boolean youGetBackWhatYouPutIn(Lens lens) { + return all(into((s, b) -> view(lens, set(lens, b, s)).equals(b)), combinations); + } + + private boolean puttingBackWhatYouGotChangesNothing(Lens lens) { + return all(into((s, b) -> set(lens, view(lens, s), s).equals(s)), combinations); + } + + private boolean settingTwiceIsEquivalentToSettingOnce(Lens lens) { + return all(into((s, b) -> set(lens, b, set(lens, b, s)).equals(set(lens, b, s))), combinations); + } + + public static LensMatcher isLawfulForAllSAndB(Iterable ss, Iterable bs) { + return new LensMatcher<>(cartesianProduct(ss, bs)); + } +} From f47b4fb64648f8352676c38a2988dec179dbe062 Mon Sep 17 00:00:00 2001 From: jnape Date: Wed, 27 Dec 2017 16:24:45 -0600 Subject: [PATCH 16/24] Adding SingletonHList#into --- .../palatable/lambda/adt/hlist/SingletonHList.java | 11 +++++++++++ .../lambda/adt/hlist/SingletonHListTest.java | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/SingletonHList.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/SingletonHList.java index edc340fbc..1dae649b5 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/SingletonHList.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/SingletonHList.java @@ -66,4 +66,15 @@ public <_1Prime, App extends Applicative> Applicative, A Function, ? extends Applicative, App>> pure) { return fn.apply(head()).fmap(SingletonHList::new); } + + /** + * Apply {@link SingletonHList#head} to fn and return the result. + * + * @param fn the function to apply + * @param the return type of the function + * @return the result of applying the head to the function + */ + public R into(Function fn) { + return fn.apply(head()); + } } diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/SingletonHListTest.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/SingletonHListTest.java index 1b4ab4bd8..56d3c19d5 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/SingletonHListTest.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/SingletonHListTest.java @@ -43,4 +43,9 @@ public void tail() { public void cons() { assertEquals(new Tuple2<>("0", singletonHList), singletonHList.cons("0")); } + + @Test + public void intoAppliesHeadToFn() { + assertEquals("FOO", singletonHList("foo").into(String::toUpperCase)); + } } \ No newline at end of file From a7fec6dafd783daf14e8dadc921423622813a9f4 Mon Sep 17 00:00:00 2001 From: jnape Date: Wed, 27 Dec 2017 16:38:25 -0600 Subject: [PATCH 17/24] Adding Into1, the standalone analog to SingletonHList#into --- .../lambda/functions/builtin/fn2/Into1.java | 37 +++++++++++++++++++ .../functions/builtin/fn2/Into1Test.java | 15 ++++++++ 2 files changed, 52 insertions(+) create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into1.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into1Test.java diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into1.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into1.java new file mode 100644 index 000000000..e965ee544 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into1.java @@ -0,0 +1,37 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import com.jnape.palatable.lambda.adt.hlist.SingletonHList; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.Fn2; + +import java.util.function.Function; + +/** + * Given a {@link Function}<A, B> and a {@link SingletonHList}<A>, + * pop the head and apply it to the function, returning the result. + * + * @param the first argument type + * @param the result type + */ +public final class Into1 implements Fn2, SingletonHList, B> { + + private static final Into1 INSTANCE = new Into1(); + + @Override + public B apply(Function fn, SingletonHList singletonHList) { + return fn.apply(singletonHList.head()); + } + + @SuppressWarnings("unchecked") + public static Into1 into1() { + return INSTANCE; + } + + public static Fn1, B> into1(Function fn) { + return Into1.into1().apply(fn); + } + + public static B into1(Function fn, SingletonHList singletonHList) { + return Into1.into1(fn).apply(singletonHList); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into1Test.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into1Test.java new file mode 100644 index 000000000..de1501fb5 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Into1Test.java @@ -0,0 +1,15 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hlist.HList.singletonHList; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Into1.into1; +import static org.junit.Assert.assertEquals; + +public class Into1Test { + + @Test + public void appliesSingletonHListHeadToFunction() { + assertEquals("FOO", into1(String::toUpperCase, singletonHList("foo"))); + } +} \ No newline at end of file From 19f2d45b00ca3f0e22cc12293d5e21cb872c9d49 Mon Sep 17 00:00:00 2001 From: jnape Date: Mon, 1 Jan 2018 22:31:19 -0500 Subject: [PATCH 18/24] adding covariant returning contramap/diMapL overrides for Bi-/Predicate --- CHANGELOG.md | 2 ++ .../functions/specialized/BiPredicate.java | 18 ++++++++++++++++++ .../functions/specialized/Predicate.java | 8 ++++++++ 3 files changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e19c491b..b764c75b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `MapLens#values` now uses defensive copies and does not alter the focused on map - `MapLens#inverted` now uses defensive copies and does not alter the focused on map - `HListLens#head` is now covariant in the tail of both `S` and `T` +- `Predicate#contraMap` is now covariant in its return type +- `BiPredicate#contraMap` and `BiPredicate#diMapL` are now both covariant in their return types ### Added - `Fn3#fn3` and `Fn4#fn4` static factory methods diff --git a/src/main/java/com/jnape/palatable/lambda/functions/specialized/BiPredicate.java b/src/main/java/com/jnape/palatable/lambda/functions/specialized/BiPredicate.java index 27d9a62ac..b7fe21c35 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/specialized/BiPredicate.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/specialized/BiPredicate.java @@ -3,6 +3,8 @@ import com.jnape.palatable.lambda.adt.hlist.Tuple2; import com.jnape.palatable.lambda.functions.Fn2; +import java.util.function.Function; + /** * A specialized {@link Fn2} that returns a Boolean when fully applied, * or a {@link Predicate} when partially applied. @@ -45,6 +47,22 @@ default Predicate> uncurry() { return Fn2.super.uncurry()::apply; } + /** + * {@inheritDoc} + */ + @Override + default BiPredicate diMapL(Function fn) { + return Fn2.super.diMapL(fn)::apply; + } + + /** + * {@inheritDoc} + */ + @Override + default Fn2 contraMap(Function fn) { + return Fn2.super.contraMap(fn)::apply; + } + /** * Override of {@link java.util.function.BiPredicate#and(java.util.function.BiPredicate)}, returning an instance of * BiPredicate for compatibility. Left-to-right composition. 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 426ce991c..89e3e5e93 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 @@ -40,6 +40,14 @@ default Predicate diMapL(Function fn) { return Fn1.super.diMapL(fn)::apply; } + /** + * {@inheritDoc} + */ + @Override + default Predicate contraMap(Function fn) { + return Fn1.super.contraMap(fn)::apply; + } + /** * Override of {@link java.util.function.Predicate#and(java.util.function.Predicate)}, returning an instance of * Predicate for compatibility. Left-to-right composition. From 3d4dc15f14d222ab479b06cf695243af8151a890 Mon Sep 17 00:00:00 2001 From: jnape Date: Sat, 6 Jan 2018 16:39:03 -0600 Subject: [PATCH 19/24] Updating README to only show build status of master --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c6635009d..191c4af42 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ λ ====== -[![Build Status](https://travis-ci.org/palatable/lambda.svg)](https://travis-ci.org/palatable/lambda) +[![Build Status](https://img.shields.io/travis/palatable/lambda/master.svg)](https://travis-ci.org/palatable/lambda) [![Lambda](https://img.shields.io/maven-central/v/com.jnape.palatable/lambda.svg)](http://search.maven.org/#search%7Cga%7C1%7Ccom.jnape.palatable.lambda) Functional patterns for Java 8 From 3745f8049acaec664055d17642708a61562f5c3c Mon Sep 17 00:00:00 2001 From: jnape Date: Sat, 13 Jan 2018 14:05:12 -0600 Subject: [PATCH 20/24] Adding missing private constructor --- .../com/jnape/palatable/lambda/functions/builtin/fn2/Snoc.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Snoc.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Snoc.java index 361cede00..7ef0cec73 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Snoc.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Snoc.java @@ -17,6 +17,9 @@ public final class Snoc implements Fn2, Iterable> { private static final Snoc INSTANCE = new Snoc(); + private Snoc() { + } + @Override public Iterable apply(A a, Iterable as) { return () -> new SnocIterator<>(a, as); From 6b7a34b340fd833f8cacb529753e1337a316e48d Mon Sep 17 00:00:00 2001 From: jnape Date: Sat, 13 Jan 2018 14:14:53 -0600 Subject: [PATCH 21/24] Adding Peek --- CHANGELOG.md | 1 + .../jnape/palatable/lambda/adt/Either.java | 4 +- .../com/jnape/palatable/lambda/adt/Maybe.java | 6 +-- .../lambda/functions/builtin/fn2/Peek.java | 44 +++++++++++++++++++ .../functions/builtin/fn2/PeekTest.java | 28 ++++++++++++ 5 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Peek.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/PeekTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index b764c75b5..4c03dc781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `CollectionLens#asSet(Function)`, a proper analog of `CollectionLens#asSet()` that uses defensive copies - `CollectionLens#asStream(Function)`, a proper analog of `CollectionLens#asStream()` that uses defensive copies - Explicitly calling attention to all unlawful lenses in their documentation +- `Peek`, for "peeking" at the value contained inside any given `Functor` with a given side-effect ### Removed - `Either#toOptional`, deprecated in previous release 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 3be69b07f..d76b6ff93 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/Either.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/Either.java @@ -1,6 +1,7 @@ package com.jnape.palatable.lambda.adt; import com.jnape.palatable.lambda.adt.coproduct.CoProduct2; +import com.jnape.palatable.lambda.functions.builtin.fn2.Peek; import com.jnape.palatable.lambda.functions.specialized.checked.CheckedFn1; import com.jnape.palatable.lambda.functions.specialized.checked.CheckedSupplier; import com.jnape.palatable.lambda.functor.Applicative; @@ -171,8 +172,7 @@ public final Either merge(BiFunction le * @return the Either, unaltered */ public Either peek(Consumer rightConsumer) { - return peek(l -> { - }, rightConsumer); + return Peek.peek(rightConsumer, this); } /** diff --git a/src/main/java/com/jnape/palatable/lambda/adt/Maybe.java b/src/main/java/com/jnape/palatable/lambda/adt/Maybe.java index 9a76139c7..9bfc7eed0 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/Maybe.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/Maybe.java @@ -1,5 +1,6 @@ package com.jnape.palatable.lambda.adt; +import com.jnape.palatable.lambda.functions.builtin.fn2.Peek; import com.jnape.palatable.lambda.functions.specialized.checked.CheckedSupplier; import com.jnape.palatable.lambda.functor.Applicative; import com.jnape.palatable.lambda.functor.Functor; @@ -145,10 +146,7 @@ public abstract Applicative, App> traverse * @return the same Maybe instance */ public final Maybe peek(Consumer consumer) { - return fmap(a -> { - consumer.accept(a); - return a; - }); + return Peek.peek(consumer, this); } /** diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Peek.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Peek.java new file mode 100644 index 000000000..59ee59cda --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Peek.java @@ -0,0 +1,44 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.Fn2; +import com.jnape.palatable.lambda.functor.Functor; + +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Given a {@link Consumer}, "peek" at the value contained inside a {@link Functor} via + * {@link Functor#fmap(Function)}, applying the {@link Consumer} to the contained value, if there is one. + * + * @param the functor parameter type + * @param the functor type + */ +public final class Peek> implements Fn2, FA, FA> { + private static final Peek INSTANCE = new Peek<>(); + + private Peek() { + } + + @Override + @SuppressWarnings("unchecked") + public FA apply(Consumer consumer, FA fa) { + return (FA) fa.fmap(a -> { + consumer.accept(a); + return a; + }); + } + + @SuppressWarnings("unchecked") + public static > Peek peek() { + return INSTANCE; + } + + public static > Fn1 peek(Consumer consumer) { + return Peek.peek().apply(consumer); + } + + public static > FA peek(Consumer consumer, FA fa) { + return Peek.peek(consumer).apply(fa); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/PeekTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/PeekTest.java new file mode 100644 index 000000000..337899584 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/PeekTest.java @@ -0,0 +1,28 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import com.jnape.palatable.lambda.adt.Maybe; +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicInteger; + +import static com.jnape.palatable.lambda.adt.Maybe.just; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Peek.peek; +import static org.junit.Assert.assertEquals; + +public class PeekTest { + + @Test + public void appliesConsumerToCarrierValue() { + AtomicInteger counter = new AtomicInteger(0); + Maybe maybeString = just("foo"); + assertEquals(maybeString, peek(x -> counter.incrementAndGet(), maybeString)); + assertEquals(1, counter.get()); + } + + @Test + public void onlyAppliesIfFmapWould() { + AtomicInteger counter = new AtomicInteger(0); + Maybe.nothing().fmap(__ -> counter.incrementAndGet()); + assertEquals(0, counter.get()); + } +} \ No newline at end of file From c6a9ff9d01287062619d4ff072aa2f8fe394fcd3 Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 14 Jan 2018 15:00:34 -0600 Subject: [PATCH 22/24] Adding Peek2 --- CHANGELOG.md | 2 +- .../jnape/palatable/lambda/adt/Either.java | 9 +-- .../lambda/functions/builtin/fn2/Peek2.java | 58 +++++++++++++++++++ .../functions/builtin/fn2/Peek2Test.java | 31 ++++++++++ 4 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Peek2.java create mode 100644 src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Peek2Test.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c03dc781..18b088c17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `CollectionLens#asSet(Function)`, a proper analog of `CollectionLens#asSet()` that uses defensive copies - `CollectionLens#asStream(Function)`, a proper analog of `CollectionLens#asStream()` that uses defensive copies - Explicitly calling attention to all unlawful lenses in their documentation -- `Peek`, for "peeking" at the value contained inside any given `Functor` with a given side-effect +- `Peek` and `Peek2`, for "peeking" at the value contained inside any given `Functor` or `Bifunctor` with given side-effects ### Removed - `Either#toOptional`, deprecated in previous release 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 d76b6ff93..b9fe4fe9b 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/Either.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/Either.java @@ -2,6 +2,7 @@ import com.jnape.palatable.lambda.adt.coproduct.CoProduct2; import com.jnape.palatable.lambda.functions.builtin.fn2.Peek; +import com.jnape.palatable.lambda.functions.builtin.fn2.Peek2; import com.jnape.palatable.lambda.functions.specialized.checked.CheckedFn1; import com.jnape.palatable.lambda.functions.specialized.checked.CheckedSupplier; import com.jnape.palatable.lambda.functor.Applicative; @@ -183,13 +184,7 @@ public Either peek(Consumer rightConsumer) { * @return the Either, unaltered */ public Either peek(Consumer leftConsumer, Consumer rightConsumer) { - return flatMap(l -> { - leftConsumer.accept(l); - return this; - }, r -> { - rightConsumer.accept(r); - return this; - }); + return Peek2.peek2(leftConsumer, rightConsumer, this); } /** diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Peek2.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Peek2.java new file mode 100644 index 000000000..83061c7c6 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Peek2.java @@ -0,0 +1,58 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.Fn2; +import com.jnape.palatable.lambda.functions.Fn3; +import com.jnape.palatable.lambda.functor.Bifunctor; + +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Given two {@link Consumer}s, "peek" at the values contained inside a {@link Bifunctor} via + * {@link Bifunctor#biMap(Function, Function)}, applying the {@link Consumer}s to the contained values, + * if there are any. + * + * @param the bifunctor's first parameter type + * @param the bifunctor's second parameter type + * @param the bifunctor type + */ +public final class Peek2> implements Fn3, Consumer, FAB, FAB> { + private static final Peek2 INSTANCE = new Peek2<>(); + + private Peek2() { + } + + @Override + @SuppressWarnings("unchecked") + public FAB apply(Consumer aConsumer, Consumer bConsumer, FAB fab) { + return (FAB) fab.biMap(a -> { + aConsumer.accept(a); + return a; + }, b -> { + bConsumer.accept(b); + return b; + }); + } + + @SuppressWarnings("unchecked") + public static > Peek2 peek2() { + return INSTANCE; + } + + public static > Fn2, FAB, FAB> peek2( + Consumer aConsumer) { + return Peek2.peek2().apply(aConsumer); + } + + public static > Fn1 peek2(Consumer aConsumer, + Consumer bConsumer) { + return Peek2.peek2(aConsumer).apply(bConsumer); + } + + public static > FAB peek2(Consumer aConsumer, + Consumer bConsumer, + FAB fab) { + return Peek2.peek2(aConsumer, bConsumer).apply(fab); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Peek2Test.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Peek2Test.java new file mode 100644 index 000000000..af16097b3 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/Peek2Test.java @@ -0,0 +1,31 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import com.jnape.palatable.lambda.adt.Either; +import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicInteger; + +import static com.jnape.palatable.lambda.adt.Either.right; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Peek2.peek2; +import static org.junit.Assert.assertEquals; + +public class Peek2Test { + + @Test + public void peeksAtBothBifunctorValues() { + AtomicInteger counter = new AtomicInteger(0); + Tuple2 tuple = tuple(1, 2); + assertEquals(tuple, peek2(__ -> counter.incrementAndGet(), __ -> counter.incrementAndGet(), tuple)); + assertEquals(2, counter.get()); + } + + @Test + public void followsSameConventionsAsBimap() { + AtomicInteger counter = new AtomicInteger(0); + Either either = right(1); + peek2(__ -> counter.incrementAndGet(), __ -> counter.incrementAndGet(), either); + assertEquals(1, counter.get()); + } +} \ No newline at end of file From 52f5a6d718fdecea70aa015553d7e03f0d7e6b0d Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 14 Jan 2018 18:52:44 -0600 Subject: [PATCH 23/24] Adding Trampoline and RecursiveResult for explicitly modeling tail-recursive functions that can be optimized to a loop by a trampoline --- CHANGELOG.md | 1 + src/main/java/spike/RecursiveResult.java | 152 +++++++++++++++++++ src/main/java/spike/Trampoline.java | 47 ++++++ src/test/java/spike/RecursiveResultTest.java | 23 +++ src/test/java/spike/TrampolineTest.java | 31 ++++ 5 files changed, 254 insertions(+) create mode 100644 src/main/java/spike/RecursiveResult.java create mode 100644 src/main/java/spike/Trampoline.java create mode 100644 src/test/java/spike/RecursiveResultTest.java create mode 100644 src/test/java/spike/TrampolineTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 18b088c17..8377d1551 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - `CollectionLens#asStream(Function)`, a proper analog of `CollectionLens#asStream()` that uses defensive copies - Explicitly calling attention to all unlawful lenses in their documentation - `Peek` and `Peek2`, for "peeking" at the value contained inside any given `Functor` or `Bifunctor` with given side-effects +- `Trampoline` and `RecursiveResult` for modeling primitive tail-recursive functions that can be trampolined ### Removed - `Either#toOptional`, deprecated in previous release diff --git a/src/main/java/spike/RecursiveResult.java b/src/main/java/spike/RecursiveResult.java new file mode 100644 index 000000000..c33c1afaa --- /dev/null +++ b/src/main/java/spike/RecursiveResult.java @@ -0,0 +1,152 @@ +package spike; + +import com.jnape.palatable.lambda.adt.coproduct.CoProduct2; +import com.jnape.palatable.lambda.functor.Applicative; +import com.jnape.palatable.lambda.functor.Bifunctor; +import com.jnape.palatable.lambda.monad.Monad; +import com.jnape.palatable.lambda.traversable.Traversable; + +import java.util.Objects; +import java.util.function.Function; + +/** + * Specialized {@link CoProduct2} representing the possible results of a primitive recursive function. + * Used by {@link Trampoline} to cheat around {@link CoProduct2#match} and quickly unpack values via + * instanceof checks to package private inner subtypes. + * + * @param the recursive function's input type + * @param the recursive function's output type + * @see Trampoline + */ +public abstract class RecursiveResult implements CoProduct2>, Bifunctor>, Monad>, Traversable> { + + private RecursiveResult() { + } + + @Override + @SuppressWarnings("unchecked") + public RecursiveResult biMapL(Function fn) { + return (RecursiveResult) Bifunctor.super.biMapL(fn); + } + + @Override + @SuppressWarnings("unchecked") + public RecursiveResult biMapR(Function fn) { + return (RecursiveResult) Bifunctor.super.biMapR(fn); + } + + @Override + public RecursiveResult biMap(Function lFn, + Function rFn) { + return match(a -> recurse(lFn.apply(a)), b -> terminate(rFn.apply(b))); + } + + @Override + public RecursiveResult flatMap( + Function>> f) { + return match(RecursiveResult::recurse, b -> f.apply(b).coerce()); + } + + @Override + public RecursiveResult pure(C c) { + return terminate(c); + } + + @Override + public RecursiveResult fmap(Function fn) { + return Monad.super.fmap(fn).coerce(); + } + + @Override + public RecursiveResult zip( + Applicative, RecursiveResult> appFn) { + return Monad.super.zip(appFn).coerce(); + } + + @Override + public RecursiveResult discardL(Applicative> appB) { + return Monad.super.discardL(appB).coerce(); + } + + @Override + public RecursiveResult discardR(Applicative> appB) { + return Monad.super.discardR(appB).coerce(); + } + + @Override + @SuppressWarnings("unchecked") + public Applicative, App> traverse( + Function> fn, + Function>, ? extends Applicative>, App>> pure) { + return match(__ -> pure.apply(coerce()).fmap(x -> (RecursiveResult) x), + b -> fn.apply(b).fmap(this::pure)); + } + + public static RecursiveResult recurse(A a) { + return new Recurse<>(a); + } + + public static RecursiveResult terminate(B b) { + return new Terminate<>(b); + } + + static final class Recurse extends RecursiveResult { + final A a; + + private Recurse(A a) { + this.a = a; + } + + @Override + public R match(Function aFn, Function bFn) { + return aFn.apply(a); + } + + @Override + public boolean equals(Object other) { + return other instanceof Recurse && Objects.equals(a, ((Recurse) other).a); + } + + @Override + public int hashCode() { + return Objects.hash(a); + } + + @Override + public String toString() { + return "Recurse{" + + "a=" + a + + '}'; + } + } + + static final class Terminate extends RecursiveResult { + final B b; + + private Terminate(B b) { + this.b = b; + } + + @Override + public R match(Function aFn, Function bFn) { + return bFn.apply(b); + } + + @Override + public boolean equals(Object other) { + return other instanceof Terminate && Objects.equals(b, ((Terminate) other).b); + } + + @Override + public int hashCode() { + return Objects.hash(b); + } + + @Override + public String toString() { + return "Terminate{" + + "b=" + b + + '}'; + } + } +} diff --git a/src/main/java/spike/Trampoline.java b/src/main/java/spike/Trampoline.java new file mode 100644 index 000000000..e6eadb4ad --- /dev/null +++ b/src/main/java/spike/Trampoline.java @@ -0,0 +1,47 @@ +package spike; + +import com.jnape.palatable.lambda.adt.coproduct.CoProduct2; +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.functions.Fn2; +import com.jnape.palatable.lambda.functions.builtin.fn2.Unfoldr; +import spike.RecursiveResult.Recurse; +import spike.RecursiveResult.Terminate; + +import java.util.function.Function; + +/** + * Given a {@link Function}<A, {@link CoProduct2}<A, B, ?>> (analogous to "recurse" and + * "return" tail position instructions, respectively), produce a {@link Function}<A, B> that + * unrolls the original function by iteratively passing each result that matches the input (A) back + * to the original function, and then terminating on and returning the first output (B). + *

+ * This is isomorphic to - though presumably faster than - taking the last element of an {@link Unfoldr} call. + * + * @param the trampolined function's input type + * @param the trampolined function's output type + */ +public final class Trampoline implements Fn2>, A, B> { + + private static final Trampoline INSTANCE = new Trampoline<>(); + + @Override + public B apply(Function> fn, A a) { + RecursiveResult next = fn.apply(a); + while (next instanceof Recurse) + next = fn.apply(((Recurse) next).a); + return ((Terminate) next).b; + } + + @SuppressWarnings("unchecked") + public static Trampoline trampoline() { + return INSTANCE; + } + + public static Fn1 trampoline(Function> fn) { + return Trampoline.trampoline().apply(fn); + } + + public static B trampoline(Function> fn, A a) { + return trampoline(fn).apply(a); + } +} diff --git a/src/test/java/spike/RecursiveResultTest.java b/src/test/java/spike/RecursiveResultTest.java new file mode 100644 index 000000000..3a7a76e7e --- /dev/null +++ b/src/test/java/spike/RecursiveResultTest.java @@ -0,0 +1,23 @@ +package spike; + +import com.jnape.palatable.traitor.annotations.TestTraits; +import com.jnape.palatable.traitor.framework.Subjects; +import com.jnape.palatable.traitor.runners.Traits; +import org.junit.runner.RunWith; +import testsupport.traits.ApplicativeLaws; +import testsupport.traits.FunctorLaws; +import testsupport.traits.MonadLaws; +import testsupport.traits.TraversableLaws; + +import static com.jnape.palatable.traitor.framework.Subjects.subjects; +import static spike.RecursiveResult.recurse; +import static spike.RecursiveResult.terminate; + +@RunWith(Traits.class) +public class RecursiveResultTest { + + @TestTraits({FunctorLaws.class, ApplicativeLaws.class, MonadLaws.class, TraversableLaws.class}) + public Subjects> testSubject() { + return subjects(recurse("foo"), terminate(1)); + } +} \ No newline at end of file diff --git a/src/test/java/spike/TrampolineTest.java b/src/test/java/spike/TrampolineTest.java new file mode 100644 index 000000000..bf8d0612e --- /dev/null +++ b/src/test/java/spike/TrampolineTest.java @@ -0,0 +1,31 @@ +package spike; + +import com.jnape.palatable.lambda.adt.hlist.Tuple2; +import org.junit.Test; + +import java.math.BigInteger; +import java.util.function.Function; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Into.into; +import static java.math.BigInteger.ONE; +import static org.junit.Assert.assertEquals; +import static spike.RecursiveResult.recurse; +import static spike.RecursiveResult.terminate; +import static spike.Trampoline.trampoline; + +public class TrampolineTest { + + private static final Function, RecursiveResult, BigInteger>> FACTORIAL = + into((x, acc) -> x.compareTo(ONE) > 0 ? recurse(tuple(x.subtract(ONE), x.multiply(acc))) : terminate(acc)); + + @Test + public void trampolinesCompatibleFunctionIntoResult() { + assertEquals(BigInteger.valueOf(3628800), trampoline(FACTORIAL, tuple(BigInteger.valueOf(10), ONE))); + } + + @Test + public void stackSafety() { + trampoline(FACTORIAL, tuple(BigInteger.valueOf(10000), ONE)); + } +} \ No newline at end of file From aa3bd221f0742ac1813e36b01ec029a7a4137993 Mon Sep 17 00:00:00 2001 From: jnape Date: Sun, 14 Jan 2018 19:21:37 -0600 Subject: [PATCH 24/24] [maven-release-plugin] prepare release lambda-2.1.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 357cc5c06..d8c05dbd7 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ lambda - 2.0.1-SNAPSHOT + 2.1.0 jar Lambda