diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 000000000..8ebcf9408
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,115 @@
+# Change Log
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/).
+
+## [Unreleased]
+
+## [1.5.6] - 2017-02-11
+### Added
+- `ChoiceN` types, representing concrete coproduct implementations that are also `Functor` and `BiFunctor`
+- `toMap`, `last`, `cons`, `prependAll`, `intersperse`
+- `Tuple2/3/4#into`, for applying the values in a tuple as positional arguments to a function.
+- `First` and `Last` monoids over `Optional`
+- `And` and `Or` monoids over `Boolean`
+
+### 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
+
+## [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
+- `eq`, `head`, `find`, and `tail`
+- `BiPredicate`
+- `Monoid#foldMap`
+- `HMap#toMap` to go from a heterogeneous map to a `java.util.Map`
+
+## [1.5.3] - 2016-11-06
+### Added
+- `Semigroup` and `Monoid`
+- `Either#invert`
+- `partition`
+- Generalized coproducts implemented as `CoProduct2` through `CoProduct5`
+- `Either` is now a `CoProduct2`
+
+## [1.5.2] - 2016-09-24
+### Added
+- Heterogeneous list indexes arrive via `Index`
+
+### Changed
+- `Lens` static factory method renaming
+
+## [1.5.1] - 2016-08-30
+### Added
+- Independent `Lens` parameter mapping via `mapS`, `mapT`, `mapA`, and `mapB`
+
+## [1.5] - 2016-08-28
+### Added
+- Initial lens support with `Lens` and `SimpleLens` types and `view`, `set`, and `over` functions
+- `Const` and `Identity` functors
+- `Either#toOptional`
+- `HMap#remove` and `HMap#removeAll`
+
+## [1.4] - 2016-08-08
+### Changed
+- 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`
+
+## [1.2] - 2016-06-27
+### Added
+- `Either#peek`
+- `HMap`, heterogeneous maps
+- `Tuple2` is now also a `Map.Entry`
+
+### Changed
+- `Tuple`s moved under `HList` as specialized subtypes
+
+## [1.1] - 2016-06-21
+### Added
+- `scanLeft`
+- `HList`, heterogenous 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
+- `all`, `any`, `cartesianProduct`, `cycle`, `drop`, `dropWhile`, `filter`, `foldLeft`,
+ `foldRight`, `inGroupsOf`, `map`, `partial2`, `partial3`, `reduceLeft`, `reduceRight`,
+ `repeat`, `take`, `takeWhile`, `unfoldr`
+- `Monadic/Dyadic/TriadicFunction`, `Predicate`, `Tuple2`, `Tuple3`
+- `Functor`, `BiFunctor`, `ProFunctor`
+
+[Unreleased]: https://github.com/palatable/lambda/compare/lambda-1.5.6...HEAD
+[1.5.6]: https://github.com/palatable/lambda/compare/lambda-1.5.5...1.5.6
+[1.5.5]: https://github.com/palatable/lambda/compare/lambda-1.5.4...lambda-1.5.5
+[1.5.4]: https://github.com/palatable/lambda/compare/lambda-1.5.3...lambda-1.5.4
+[1.5.3]: https://github.com/palatable/lambda/compare/lambda-1.5.2...lambda-1.5.3
+[1.5.2]: https://github.com/palatable/lambda/compare/lambda-1.5.1...lambda-1.5.2
+[1.5.1]: https://github.com/palatable/lambda/compare/lambda-1.5...lambda-1.5.1
+[1.5]: https://github.com/palatable/lambda/compare/lambda-1.4...lambda-1.5
+[1.4]: https://github.com/palatable/lambda/compare/lambda-1.3...lambda-1.4
+[1.3]: https://github.com/palatable/lambda/compare/lambda-1.2...lambda-1.3
+[1.2]: https://github.com/palatable/lambda/compare/lambda-1.1...lambda-1.2
+[1.1]: https://github.com/palatable/lambda/compare/lambda-1.0...lambda-1.1
+[1.0]: https://github.com/palatable/lambda/commits/lambda-1.0
\ No newline at end of file
diff --git a/README.md b/README.md
index 4d7cb8af1..31c2fa53e 100644
--- a/README.md
+++ b/README.md
@@ -48,16 +48,15 @@ Add the following dependency to your:
com.jnape.palatable
lambda
- 1.5.4
+ 1.5.6
```
`build.gradle` ([Gradle](https://docs.gradle.org/current/userguide/dependency_management.html)):
```gradle
-compile group: 'com.jnape.palatable', name: 'lambda', version: '1.5.4'
+compile group: 'com.jnape.palatable', name: 'lambda', version: '1.5.6'
```
-
Examples
--------
@@ -237,20 +236,20 @@ Optional anotherIntValue = hmap.get(anotherIntKey); // Optional.empty
### CoProducts
-`CoProduct`s generalize unions of disparate types in a single consolidated type.
+`CoProduct`s generalize unions of disparate types in a single consolidated type, and the `ChoiceN` ADTs represent canonical implementations of these coproduct types.
```Java
-CoProduct3 string = CoProduct3.a("string");
-CoProduct3 integer = CoProduct3.b(1);
-CoProduct3 character = CoProduct3.c('a');
+CoProduct3 string = Choice3.a("string");
+CoProduct3 integer = Choice3.b(1);
+CoProduct3 character = Choice3.c('a');
```
Rather than supporting explicit value unwrapping, which would necessarily jeopardize type safety, `CoProduct`s support a `match` method that takes one function per possible value type and maps it to a final common result type:
```Java
-CoProduct3 string = CoProduct3.a("string");
-CoProduct3 integer = CoProduct3.b(1);
-CoProduct3 character = CoProduct3.c('a');
+CoProduct3 string = Choice3.a("string");
+CoProduct3 integer = Choice3.b(1);
+CoProduct3 character = Choice3.c('a');
Integer result = string.match(String::length, identity(), Character::charCount); // 6
```
@@ -258,11 +257,11 @@ Integer result = string.match(String::length, identity(), Character::ch
Additionally, because a `CoProduct2` guarantees a subset of a `CoProduct3`, the `diverge` method exists between `CoProduct` types of single magnitude differences to make it easy to use a more convergent `CoProduct` where a more divergent `CoProduct` is expected:
```Java
-CoProduct2 coProduct2 = CoProduct2.a("string");
+CoProduct2 coProduct2 = Choice2.a("string");
CoProduct3 coProduct3 = coProduct2.diverge(); // still just the coProduct2 value, adapted to the coProduct3 shape
```
-There are `CoProduct` specializations for type unions of up to 5 different types: `CoProduct2` through `CoProduct5`, respectively.
+There are `CoProduct` and `Choice` specializations for type unions of up to 5 different types: `CoProduct2` through `CoProduct5`, and `Choice2` through `Choice5`, respectively.
### Either
diff --git a/pom.xml b/pom.xml
index 67c73e480..fb88cf903 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
lambda
- 1.5.5
+ 1.5.6
jar
Lambda
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 4c6c70d57..4d6c988c4 100644
--- a/src/main/java/com/jnape/palatable/lambda/adt/Either.java
+++ b/src/main/java/com/jnape/palatable/lambda/adt/Either.java
@@ -3,6 +3,8 @@
import com.jnape.palatable.lambda.adt.coproduct.CoProduct2;
import com.jnape.palatable.lambda.functions.specialized.checked.CheckedFn1;
import com.jnape.palatable.lambda.functions.specialized.checked.CheckedSupplier;
+import com.jnape.palatable.lambda.functor.Bifunctor;
+import com.jnape.palatable.lambda.functor.Functor;
import java.util.Objects;
import java.util.Optional;
@@ -23,7 +25,7 @@
* @param The left parameter type
* @param The right parameter type
*/
-public abstract class Either implements CoProduct2 {
+public abstract class Either implements CoProduct2, Functor, Bifunctor {
private Either() {
}
@@ -146,8 +148,8 @@ public final Either invert() {
public final Either merge(BiFunction super L, ? super L, ? extends L> leftFn,
BiFunction super R, ? super R, ? extends R> rightFn,
Either... others) {
- return foldLeft((x, y) -> x.match(l1 -> y.>match(l2 -> left(leftFn.apply(l1, l2)), r -> left(l1)),
- r1 -> y.>match(Either::left, r2 -> right(rightFn.apply(r1, r2)))),
+ return foldLeft((x, y) -> x.match(l1 -> y.match(l2 -> left(leftFn.apply(l1, l2)), r -> left(l1)),
+ r1 -> y.match(Either::left, r2 -> right(rightFn.apply(r1, r2)))),
this,
asList(others));
}
@@ -200,13 +202,13 @@ public final Either fmap(Function super R, ? extends R2> fn) {
@Override
@SuppressWarnings("unchecked")
public final Either biMapL(Function super L, ? extends L2> fn) {
- return (Either) CoProduct2.super.biMapL(fn);
+ return (Either) Bifunctor.super.biMapL(fn);
}
@Override
@SuppressWarnings("unchecked")
public final Either biMapR(Function super R, ? extends R2> fn) {
- return (Either) CoProduct2.super.biMapR(fn);
+ return (Either) Bifunctor.super.biMapR(fn);
}
@Override
@@ -237,7 +239,7 @@ public Optional toOptional() {
*/
public static Either fromOptional(Optional optional, Supplier leftFn) {
return optional.>map(Either::right)
- .orElse(left(leftFn.get()));
+ .orElseGet(() -> left(leftFn.get()));
}
/**
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
new file mode 100644
index 000000000..2fe7a03ed
--- /dev/null
+++ b/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice2.java
@@ -0,0 +1,141 @@
+package com.jnape.palatable.lambda.adt.choice;
+
+import com.jnape.palatable.lambda.adt.Either;
+import com.jnape.palatable.lambda.adt.coproduct.CoProduct2;
+import com.jnape.palatable.lambda.functor.Bifunctor;
+import com.jnape.palatable.lambda.functor.Functor;
+
+import java.util.Objects;
+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.
+ *
+ * @param a type parameter representing the first possible type of this choice
+ * @param a type parameter representing the second possible type of this choice
+ * @see Either
+ * @see Choice3
+ */
+public abstract class Choice2 implements CoProduct2, Functor, Bifunctor {
+
+ private Choice2() {
+ }
+
+ @Override
+ public final Choice3 diverge() {
+ return match(Choice3::a, Choice3::b);
+ }
+
+ @Override
+ public final Choice2 fmap(Function super B, ? extends C> fn) {
+ return biMapR(fn);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final Choice2 biMapL(Function super A, ? extends C> fn) {
+ return (Choice2) Bifunctor.super.biMapL(fn);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final Choice2 biMapR(Function super B, ? extends C> fn) {
+ return (Choice2) Bifunctor.super.biMapR(fn);
+ }
+
+ @Override
+ public final Choice2 biMap(Function super A, ? extends C> lFn,
+ Function super B, ? extends D> rFn) {
+ return match(a -> a(lFn.apply(a)), b -> b(rFn.apply(b)));
+ }
+
+ /**
+ * 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>
+ */
+ public static Choice2 a(A a) {
+ return new _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>
+ */
+ public static Choice2 b(B b) {
+ return new _B<>(b);
+ }
+
+ private static final class _A extends Choice2 {
+
+ private final A a;
+
+ private _A(A a) {
+ this.a = a;
+ }
+
+ @Override
+ public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn) {
+ 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 "Choice2{" +
+ "a=" + a +
+ '}';
+ }
+ }
+
+ private static final class _B extends Choice2 {
+
+ private final B b;
+
+ private _B(B b) {
+ this.b = b;
+ }
+
+ @Override
+ public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn) {
+ 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 "Choice2{" +
+ "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
new file mode 100644
index 000000000..52119c199
--- /dev/null
+++ b/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice3.java
@@ -0,0 +1,197 @@
+package com.jnape.palatable.lambda.adt.choice;
+
+import com.jnape.palatable.lambda.adt.coproduct.CoProduct2;
+import com.jnape.palatable.lambda.adt.coproduct.CoProduct3;
+import com.jnape.palatable.lambda.functor.Bifunctor;
+import com.jnape.palatable.lambda.functor.Functor;
+
+import java.util.Objects;
+import java.util.function.Function;
+
+/**
+ * Canonical ADT representation of {@link CoProduct3} that is also a {@link Functor} and {@link Bifunctor}.
+ *
+ * @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
+ * @see Choice2
+ * @see Choice4
+ */
+public abstract class Choice3 implements CoProduct3, Functor, Bifunctor {
+
+ private Choice3() {
+ }
+
+ @Override
+ public final Choice4 diverge() {
+ return match(Choice4::a, Choice4::b, Choice4::c);
+ }
+
+ @Override
+ public final Choice2 converge(Function super C, ? extends CoProduct2> convergenceFn) {
+ return match(Choice2::a, Choice2::b, convergenceFn.andThen(cp2 -> cp2.match(Choice2::a, Choice2::b)));
+ }
+
+ @Override
+ public final Choice3 fmap(Function super C, ? extends D> fn) {
+ return biMapR(fn);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final Choice3 biMapL(Function super B, ? extends D> fn) {
+ return (Choice3) Bifunctor.super.biMapL(fn);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final Choice3 biMapR(Function super C, ? extends D> fn) {
+ return (Choice3) Bifunctor.super.biMapR(fn);
+ }
+
+ @Override
+ public final Choice3 biMap(Function super B, ? extends D> lFn,
+ Function super C, ? extends E> rFn) {
+ return match(Choice3::a, b -> b(lFn.apply(b)), c -> c(rFn.apply(c)));
+ }
+
+ /**
+ * 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>
+ */
+ public static Choice3 a(A a) {
+ return new _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>
+ */
+ public static Choice3 b(B b) {
+ return new _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>
+ */
+ public static Choice3 c(C c) {
+ return new _C<>(c);
+ }
+
+ private static final class _A extends Choice3 {
+
+ private final A a;
+
+ private _A(A a) {
+ this.a = a;
+ }
+
+ @Override
+ public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn,
+ Function super C, ? extends R> cFn) {
+ 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 "Choice3{" +
+ "a=" + a +
+ '}';
+ }
+ }
+
+ private static final class _B extends Choice3 {
+
+ private final B b;
+
+ private _B(B b) {
+ this.b = b;
+ }
+
+ @Override
+ public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn,
+ Function super C, ? extends R> cFn) {
+ 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 "Choice3{" +
+ "b=" + b +
+ '}';
+ }
+ }
+
+ private static final class _C extends Choice3 {
+
+ private final C c;
+
+ private _C(C c) {
+ this.c = c;
+ }
+
+ @Override
+ public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn,
+ Function super C, ? extends R> cFn) {
+ 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 "Choice3{" +
+ "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
new file mode 100644
index 000000000..d9ef5efd6
--- /dev/null
+++ b/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice4.java
@@ -0,0 +1,249 @@
+package com.jnape.palatable.lambda.adt.choice;
+
+import com.jnape.palatable.lambda.adt.coproduct.CoProduct3;
+import com.jnape.palatable.lambda.adt.coproduct.CoProduct4;
+import com.jnape.palatable.lambda.functor.Bifunctor;
+import com.jnape.palatable.lambda.functor.Functor;
+
+import java.util.Objects;
+import java.util.function.Function;
+
+/**
+ * Canonical ADT representation of {@link CoProduct4} that is also a {@link Functor} and {@link Bifunctor}.
+ *
+ * @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
+ * @see Choice3
+ * @see Choice5
+ */
+public abstract class Choice4 implements CoProduct4, Functor, Bifunctor {
+
+ private Choice4() {
+ }
+
+ @Override
+ public Choice5 diverge() {
+ return match(Choice5::a, Choice5::b, Choice5::c, Choice5::d);
+ }
+
+ @Override
+ public Choice3 converge(Function super D, ? extends CoProduct3> convergenceFn) {
+ return match(Choice3::a,
+ Choice3::b,
+ Choice3::c,
+ convergenceFn.andThen(cp3 -> cp3.match(Choice3::a, Choice3::b, Choice3::c)));
+ }
+
+ @Override
+ public final Choice4 fmap(Function super D, ? extends E> fn) {
+ return biMapR(fn);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final Choice4 biMapL(Function super C, ? extends E> fn) {
+ return (Choice4) Bifunctor.super.biMapL(fn);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final Choice4 biMapR(Function super D, ? extends E> fn) {
+ return (Choice4) Bifunctor.super.biMapR(fn);
+ }
+
+ @Override
+ public final Choice4 biMap(Function super C, ? extends E> lFn,
+ Function super D, ? extends F> rFn) {
+ return match(Choice4::a, Choice4::b, c -> c(lFn.apply(c)), d -> d(rFn.apply(d)));
+ }
+
+ /**
+ * 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>
+ */
+ public static Choice4 a(A a) {
+ return new _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>
+ */
+ public static Choice4 b(B b) {
+ return new _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>
+ */
+ public static Choice4 c(C c) {
+ return new _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>
+ */
+ public static Choice4 d(D d) {
+ return new _D<>(d);
+ }
+
+ private static final class _A extends Choice4 {
+
+ private final A a;
+
+ private _A(A a) {
+ this.a = a;
+ }
+
+ @Override
+ public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn,
+ Function super C, ? extends R> cFn, Function super D, ? extends R> dFn) {
+ 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 "Choice4{" +
+ "a=" + a +
+ '}';
+ }
+ }
+
+ private static final class _B extends Choice4 {
+
+ private final B b;
+
+ private _B(B b) {
+ this.b = b;
+ }
+
+ @Override
+ public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn,
+ Function super C, ? extends R> cFn, Function super D, ? extends R> dFn) {
+ 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 "Choice4{" +
+ "b=" + b +
+ '}';
+ }
+ }
+
+ private static final class _C extends Choice4 {
+
+ private final C c;
+
+ private _C(C c) {
+ this.c = c;
+ }
+
+ @Override
+ public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn,
+ Function super C, ? extends R> cFn, Function super D, ? extends R> dFn) {
+ 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 "Choice4{" +
+ "c=" + c +
+ '}';
+ }
+ }
+
+ private static final class _D extends Choice4 {
+
+ private final D d;
+
+ private _D(D d) {
+ this.d = d;
+ }
+
+ @Override
+ public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn,
+ Function super C, ? extends R> cFn, Function super D, ? extends R> dFn) {
+ 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 "Choice4{" +
+ "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
new file mode 100644
index 000000000..5603a4fe6
--- /dev/null
+++ b/src/main/java/com/jnape/palatable/lambda/adt/choice/Choice5.java
@@ -0,0 +1,303 @@
+package com.jnape.palatable.lambda.adt.choice;
+
+import com.jnape.palatable.lambda.adt.coproduct.CoProduct4;
+import com.jnape.palatable.lambda.adt.coproduct.CoProduct5;
+import com.jnape.palatable.lambda.functor.Bifunctor;
+import com.jnape.palatable.lambda.functor.Functor;
+
+import java.util.Objects;
+import java.util.function.Function;
+
+/**
+ * Canonical ADT representation of {@link CoProduct5} that is also a {@link Functor} and {@link Bifunctor}.
+ *
+ * @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
+ * @see Choice4
+ */
+public abstract class Choice5 implements CoProduct5, Functor, Bifunctor {
+
+ private Choice5() {
+ }
+
+ @Override
+ public Choice4 converge(Function super E, ? extends CoProduct4> convergenceFn) {
+ return match(Choice4::a,
+ Choice4::b,
+ Choice4::c,
+ Choice4::d,
+ convergenceFn.andThen(cp4 -> cp4.match(Choice4::a, Choice4::b, Choice4::c, Choice4::d)));
+ }
+
+ @Override
+ public Choice5 fmap(Function super E, ? extends F> fn) {
+ return biMapR(fn);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Choice5 biMapL(Function super D, ? extends F> fn) {
+ return (Choice5) Bifunctor.super.biMapL(fn);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Choice5 biMapR(Function super E, ? extends F> fn) {
+ return (Choice5) Bifunctor.super.biMapR(fn);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Choice5 biMap(Function super D, ? extends F> lFn,
+ Function super E, ? extends G> rFn) {
+ return match(Choice5::a, Choice5::b, Choice5::c, d -> d(lFn.apply(d)), e -> e(rFn.apply(e)));
+ }
+
+ /**
+ * 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>
+ */
+ public static Choice5 a(A a) {
+ return new _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>
+ */
+ public static Choice5 b(B b) {
+ return new _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>
+ */
+ public static Choice5 c(C c) {
+ return new _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>
+ */
+ public static Choice5 d(D d) {
+ return new _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>
+ */
+ public static Choice5 e(E e) {
+ return new _E<>(e);
+ }
+
+ private static final class _A extends Choice5 {
+
+ private final A a;
+
+ private _A(A a) {
+ this.a = a;
+ }
+
+ @Override
+ public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn,
+ Function super C, ? extends R> cFn, Function super D, ? extends R> dFn,
+ Function super E, ? extends R> eFn) {
+ 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 "Choice5{" +
+ "a=" + a +
+ '}';
+ }
+ }
+
+ private static final class _B extends Choice5 {
+
+ private final B b;
+
+ private _B(B b) {
+ this.b = b;
+ }
+
+ @Override
+ public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn,
+ Function super C, ? extends R> cFn, Function super D, ? extends R> dFn,
+ Function super E, ? extends R> eFn) {
+ 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 "Choice5{" +
+ "b=" + b +
+ '}';
+ }
+ }
+
+ private static final class _C extends Choice5 {
+
+ private final C c;
+
+ private _C(C c) {
+ this.c = c;
+ }
+
+ @Override
+ public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn,
+ Function super C, ? extends R> cFn, Function super D, ? extends R> dFn,
+ Function super E, ? extends R> eFn) {
+ 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 "Choice5{" +
+ "c=" + c +
+ '}';
+ }
+ }
+
+ private static final class _D extends Choice5 {
+
+ private final D d;
+
+ private _D(D d) {
+ this.d = d;
+ }
+
+ @Override
+ public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn,
+ Function super C, ? extends R> cFn, Function super D, ? extends R> dFn,
+ Function super E, ? extends R> eFn) {
+ 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 "Choice5{" +
+ "d=" + d +
+ '}';
+ }
+ }
+
+ private static final class _E extends Choice5 {
+
+ private final E e;
+
+ private _E(E e) {
+ this.e = e;
+ }
+
+ @Override
+ public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn,
+ Function super C, ? extends R> cFn, Function super D, ? extends R> dFn,
+ Function super E, ? extends R> eFn) {
+ 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 "Choice5{" +
+ "e=" + e +
+ '}';
+ }
+ }
+}
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 66684acde..77f347412 100644
--- a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2.java
+++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2.java
@@ -1,10 +1,9 @@
package com.jnape.palatable.lambda.adt.coproduct;
+import com.jnape.palatable.lambda.adt.Either;
+import com.jnape.palatable.lambda.adt.choice.Choice2;
import com.jnape.palatable.lambda.adt.hlist.Tuple2;
-import com.jnape.palatable.lambda.functor.Bifunctor;
-import com.jnape.palatable.lambda.functor.Functor;
-import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
@@ -19,9 +18,11 @@
*
* @param a type parameter representing the first possible type of this coproduct
* @param a type parameter representing the second possible type of this coproduct
+ * @see Choice2
+ * @see Either
*/
@FunctionalInterface
-public interface CoProduct2 extends Functor, Bifunctor {
+public interface CoProduct2 {
/**
* Type-safe convergence requiring a match against all potential types.
@@ -55,7 +56,13 @@ public interface CoProduct2 extends Functor, Bifunctor {
* @return a coproduct of the initial types plus the new type
*/
default CoProduct3 diverge() {
- return match(CoProduct3::a, CoProduct3::b);
+ return new CoProduct3() {
+ @Override
+ public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn,
+ Function super C, ? extends R> cFn) {
+ return CoProduct2.this.match(aFn, bFn);
+ }
+ };
}
/**
@@ -74,7 +81,6 @@ default Tuple2, Optional> project() {
*
* @return an optional value representing the projection of the "a" type index
*/
- @SuppressWarnings("unused")
default Optional projectA() {
return project()._1();
}
@@ -84,118 +90,7 @@ default Optional projectA() {
*
* @return an optional value representing the projection of the "b" type index
*/
- @SuppressWarnings("unused")
default Optional projectB() {
return project()._2();
}
-
- @Override
- default CoProduct2 fmap(Function super B, ? extends C> fn) {
- return biMapR(fn);
- }
-
- @Override
- @SuppressWarnings("unchecked")
- default CoProduct2 biMapL(Function super A, ? extends C> fn) {
- return (CoProduct2) Bifunctor.super.biMapL(fn);
- }
-
- @Override
- @SuppressWarnings("unchecked")
- default CoProduct2 biMapR(Function super B, ? extends C> fn) {
- return (CoProduct2) Bifunctor.super.biMapR(fn);
- }
-
- @Override
- default CoProduct2 biMap(Function super A, ? extends C> lFn,
- Function super B, ? extends D> rFn) {
- return match(a -> a(lFn.apply(a)), b -> b(rFn.apply(b)));
- }
-
- /**
- * Static factory method for wrapping a value of type A in a {@link CoProduct2}.
- *
- * @param a the value
- * @param a type parameter representing the first possible type of this coproduct
- * @param a type parameter representing the second possible type of this coproduct
- * @return the wrapped value as a CoProduct2<A, B>
- */
- static CoProduct2 a(A a) {
- class _A implements CoProduct2 {
-
- private final A a;
-
- private _A(A a) {
- this.a = a;
- }
-
- @Override
- public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn) {
- 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 "CoProduct2{" +
- "a=" + a +
- '}';
- }
- }
-
- return new _A(a);
- }
-
- /**
- * Static factory method for wrapping a value of type B in a {@link CoProduct2}.
- *
- * @param b the value
- * @param a type parameter representing the first possible type of this coproduct
- * @param a type parameter representing the second possible type of this coproduct
- * @return the wrapped value as a CoProduct2<A, B>
- */
- static CoProduct2 b(B b) {
- class _B implements CoProduct2 {
-
- private final B b;
-
- private _B(B b) {
- this.b = b;
- }
-
- @Override
- public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn) {
- 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 "CoProduct2{" +
- "b=" + b +
- '}';
- }
- }
- return new _B(b);
- }
}
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 3b5047c45..1bc4312d7 100644
--- a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3.java
+++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct3.java
@@ -1,10 +1,7 @@
package com.jnape.palatable.lambda.adt.coproduct;
import com.jnape.palatable.lambda.adt.hlist.Tuple3;
-import com.jnape.palatable.lambda.functor.Bifunctor;
-import com.jnape.palatable.lambda.functor.Functor;
-import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
@@ -19,7 +16,7 @@
* @see CoProduct2
*/
@FunctionalInterface
-public interface CoProduct3 extends Functor, Bifunctor {
+public interface CoProduct3 {
/**
* Type-safe convergence requiring a match against all potential types.
@@ -42,7 +39,13 @@ R match(Function super A, ? extends R> aFn, Function super B, ? extends
* @see CoProduct2#diverge()
*/
default CoProduct4 diverge() {
- return match(CoProduct4::a, CoProduct4::b, CoProduct4::c);
+ return new CoProduct4() {
+ @Override
+ public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn,
+ Function super C, ? extends R> cFn, Function super D, ? extends R> dFn) {
+ return CoProduct3.this.match(aFn, bFn, cFn);
+ }
+ };
}
/**
@@ -59,7 +62,17 @@ default CoProduct4 diverge() {
* @return a coproduct of the initial types without the terminal type
*/
default CoProduct2 converge(Function super C, ? extends CoProduct2> convergenceFn) {
- return match(CoProduct2::a, CoProduct2::b, convergenceFn);
+ return match(a -> new CoProduct2() {
+ @Override
+ public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn) {
+ return aFn.apply(a);
+ }
+ }, b -> new CoProduct2() {
+ @Override
+ public R match(Function super A, ? extends R> aFn, Function super B, ? extends R> bFn) {
+ return bFn.apply(b);
+ }
+ }, convergenceFn);
}
/**
@@ -79,7 +92,6 @@ default Tuple3, Optional, Optional> project() {
*
* @return an optional value representing the projection of the "a" type index
*/
- @SuppressWarnings("unused")
default Optional projectA() {
return project()._1();
}
@@ -89,7 +101,6 @@ default Optional projectA() {
*
* @return an optional value representing the projection of the "b" type index
*/
- @SuppressWarnings("unused")
default Optional projectB() {
return project()._2();
}
@@ -99,169 +110,7 @@ default Optional projectB() {
*
* @return an optional value representing the projection of the "c" type index
*/
- @SuppressWarnings("unused")
default Optional projectC() {
return project()._3();
}
-
- @Override
- default CoProduct3 fmap(Function super C, ? extends D> fn) {
- return biMapR(fn);
- }
-
- @Override
- @SuppressWarnings("unchecked")
- default CoProduct3 biMapL(Function super B, ? extends D> fn) {
- return (CoProduct3) Bifunctor.super.biMapL(fn);
- }
-
- @Override
- @SuppressWarnings("unchecked")
- default CoProduct3 biMapR(Function super C, ? extends D> fn) {
- return (CoProduct3) Bifunctor.super.biMapR(fn);
- }
-
- @Override
- default CoProduct3 biMap(Function super B, ? extends D> lFn,
- Function super C, ? extends E> rFn) {
- return match(CoProduct3::a, b -> b(lFn.apply(b)), c -> c(rFn.apply(c)));
- }
-
- /**
- * Static factory method for wrapping a value of type A in a {@link CoProduct3}.
- *
- * @param a the value
- * @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
- * @return the wrapped value as a CoProduct3<A, B, C>
- */
- static