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 leftFn, BiFunction 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 fn) { @Override @SuppressWarnings("unchecked") public final Either biMapL(Function fn) { - return (Either) CoProduct2.super.biMapL(fn); + return (Either) Bifunctor.super.biMapL(fn); } @Override @SuppressWarnings("unchecked") public final Either biMapR(Function 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 fn) { + return biMapR(fn); + } + + @Override + @SuppressWarnings("unchecked") + public final Choice2 biMapL(Function fn) { + return (Choice2) Bifunctor.super.biMapL(fn); + } + + @Override + @SuppressWarnings("unchecked") + public final Choice2 biMapR(Function fn) { + return (Choice2) Bifunctor.super.biMapR(fn); + } + + @Override + public final Choice2 biMap(Function lFn, + Function 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 aFn, Function 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 aFn, Function 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> convergenceFn) { + return match(Choice2::a, Choice2::b, convergenceFn.andThen(cp2 -> cp2.match(Choice2::a, Choice2::b))); + } + + @Override + public final Choice3 fmap(Function fn) { + return biMapR(fn); + } + + @Override + @SuppressWarnings("unchecked") + public final Choice3 biMapL(Function fn) { + return (Choice3) Bifunctor.super.biMapL(fn); + } + + @Override + @SuppressWarnings("unchecked") + public final Choice3 biMapR(Function fn) { + return (Choice3) Bifunctor.super.biMapR(fn); + } + + @Override + public final Choice3 biMap(Function lFn, + Function 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 aFn, Function bFn, + Function 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 aFn, Function bFn, + Function 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 aFn, Function bFn, + Function 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> 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 fn) { + return biMapR(fn); + } + + @Override + @SuppressWarnings("unchecked") + public final Choice4 biMapL(Function fn) { + return (Choice4) Bifunctor.super.biMapL(fn); + } + + @Override + @SuppressWarnings("unchecked") + public final Choice4 biMapR(Function fn) { + return (Choice4) Bifunctor.super.biMapR(fn); + } + + @Override + public final Choice4 biMap(Function lFn, + Function 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 aFn, Function bFn, + Function cFn, Function 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 aFn, Function bFn, + Function cFn, Function 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 aFn, Function bFn, + Function cFn, Function 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 aFn, Function bFn, + Function cFn, Function 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> 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 fn) { + return biMapR(fn); + } + + @Override + @SuppressWarnings("unchecked") + public Choice5 biMapL(Function fn) { + return (Choice5) Bifunctor.super.biMapL(fn); + } + + @Override + @SuppressWarnings("unchecked") + public Choice5 biMapR(Function fn) { + return (Choice5) Bifunctor.super.biMapR(fn); + } + + @Override + @SuppressWarnings("unchecked") + public Choice5 biMap(Function lFn, + Function 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 aFn, Function bFn, + Function cFn, Function dFn, + Function 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 aFn, Function bFn, + Function cFn, Function dFn, + Function 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 aFn, Function bFn, + Function cFn, Function dFn, + Function 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 aFn, Function bFn, + Function cFn, Function dFn, + Function 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 aFn, Function bFn, + Function cFn, Function dFn, + Function 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 aFn, Function bFn, + Function 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 fn) { - return biMapR(fn); - } - - @Override - @SuppressWarnings("unchecked") - default CoProduct2 biMapL(Function fn) { - return (CoProduct2) Bifunctor.super.biMapL(fn); - } - - @Override - @SuppressWarnings("unchecked") - default CoProduct2 biMapR(Function fn) { - return (CoProduct2) Bifunctor.super.biMapR(fn); - } - - @Override - default CoProduct2 biMap(Function lFn, - Function 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 aFn, Function 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 aFn, Function 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 aFn, Function CoProduct4 diverge() { - return match(CoProduct4::a, CoProduct4::b, CoProduct4::c); + return new CoProduct4() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function 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> convergenceFn) { - return match(CoProduct2::a, CoProduct2::b, convergenceFn); + return match(a -> new CoProduct2() { + @Override + public R match(Function aFn, Function bFn) { + return aFn.apply(a); + } + }, b -> new CoProduct2() { + @Override + public R match(Function aFn, Function 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 fn) { - return biMapR(fn); - } - - @Override - @SuppressWarnings("unchecked") - default CoProduct3 biMapL(Function fn) { - return (CoProduct3) Bifunctor.super.biMapL(fn); - } - - @Override - @SuppressWarnings("unchecked") - default CoProduct3 biMapR(Function fn) { - return (CoProduct3) Bifunctor.super.biMapR(fn); - } - - @Override - default CoProduct3 biMap(Function lFn, - Function 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 CoProduct3 a(A a) { - class _A implements CoProduct3 { - - private final A a; - - private _A(A a) { - this.a = a; - } - - @Override - public R match(Function aFn, Function bFn, - Function 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 "CoProduct3{" + - "a=" + a + - '}'; - } - } - - return new _A(a); - } - - /** - * Static factory method for wrapping a value of type A in a {@link CoProduct3}. - * - * @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 - * @param a type parameter representing the third possible type of this coproduct - * @return the wrapped value as a CoProduct3<A, B, C> - */ - static CoProduct3 b(B b) { - class _B implements CoProduct3 { - - private final B b; - - private _B(B b) { - this.b = b; - } - - @Override - public R match(Function aFn, Function bFn, - Function 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 "CoProduct3{" + - "b=" + b + - '}'; - } - } - - return new _B(b); - } - - /** - * Static factory method for wrapping a value of type A in a {@link CoProduct3}. - * - * @param c 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 CoProduct3 c(C c) { - class _C implements CoProduct3 { - - private final C c; - - private _C(C c) { - this.c = c; - } - - @Override - public R match(Function aFn, Function bFn, - Function 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 "CoProduct3{" + - "c=" + c + - '}'; - } - } - - return new _C(c); - } } 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 466a43509..739252a54 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct4.java @@ -1,10 +1,7 @@ package com.jnape.palatable.lambda.adt.coproduct; import com.jnape.palatable.lambda.adt.hlist.Tuple4; -import com.jnape.palatable.lambda.functor.Bifunctor; -import com.jnape.palatable.lambda.functor.Functor; -import java.util.Objects; import java.util.Optional; import java.util.function.Function; @@ -20,7 +17,7 @@ * @see CoProduct2 */ @FunctionalInterface -public interface CoProduct4 extends Functor, Bifunctor { +public interface CoProduct4 { /** * Type-safe convergence requiring a match against all potential types. @@ -46,7 +43,14 @@ R match(Function aFn, * @see CoProduct2#diverge() */ default CoProduct5 diverge() { - return match(CoProduct5::a, CoProduct5::b, CoProduct5::c, CoProduct5::d); + return new CoProduct5() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn) { + return CoProduct4.this.match(aFn, bFn, cFn, dFn); + } + }; } /** @@ -58,7 +62,25 @@ default CoProduct5 diverge() { * @see CoProduct3#converge */ default CoProduct3 converge(Function> convergenceFn) { - return match(CoProduct3::a, CoProduct3::b, CoProduct3::c, convergenceFn); + return match(a -> new CoProduct3() { + @Override + public R match(Function aFn, Function bFn, + Function cFn) { + return aFn.apply(a); + } + }, b -> new CoProduct3() { + @Override + public R match(Function aFn, Function bFn, + Function cFn) { + return bFn.apply(b); + } + }, c -> new CoProduct3() { + @Override + public R match(Function aFn, Function bFn, + Function cFn) { + return cFn.apply(c); + } + }, convergenceFn); } /** @@ -79,7 +101,6 @@ default Tuple4, Optional, 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 +110,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,7 +119,6 @@ default Optional projectB() { * * @return an optional value representing the projection of the "c" type index */ - @SuppressWarnings("unused") default Optional projectC() { return project()._3(); } @@ -109,217 +128,7 @@ default Optional projectC() { * * @return an optional value representing the projection of the "d" type index */ - @SuppressWarnings("unused") default Optional projectD() { return project()._4(); } - - @Override - default CoProduct4 fmap(Function fn) { - return biMapR(fn); - } - - @Override - @SuppressWarnings("unchecked") - default CoProduct4 biMapL(Function fn) { - return (CoProduct4) Bifunctor.super.biMapL(fn); - } - - @Override - @SuppressWarnings("unchecked") - default CoProduct4 biMapR(Function fn) { - return (CoProduct4) Bifunctor.super.biMapR(fn); - } - - @Override - default CoProduct4 biMap(Function lFn, - Function rFn) { - return match(CoProduct4::a, CoProduct4::b, c -> c(lFn.apply(c)), d -> d(rFn.apply(d))); - } - - /** - * Static factory method for wrapping a value of type A in a {@link CoProduct4}. - * - * @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 - * @param a type parameter representing the fourth possible type of this coproduct - * @return the wrapped value as a CoProduct4<A, B, C, D> - */ - static CoProduct4 a(A a) { - class _A implements CoProduct4 { - - private final A a; - - private _A(A a) { - this.a = a; - } - - @Override - public R match(Function aFn, Function bFn, - Function cFn, Function 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 "CoProduct4{" + - "a=" + a + - '}'; - } - } - - return new _A(a); - } - - /** - * Static factory method for wrapping a value of type B in a {@link CoProduct4}. - * - * @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 - * @param a type parameter representing the third possible type of this coproduct - * @param a type parameter representing the fourth possible type of this coproduct - * @return the wrapped value as a CoProduct4<A, B, C, D> - */ - static CoProduct4 b(B b) { - class _B implements CoProduct4 { - - private final B b; - - private _B(B b) { - this.b = b; - } - - @Override - public R match(Function aFn, Function bFn, - Function cFn, Function 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 "CoProduct4{" + - "b=" + b + - '}'; - } - } - return new _B(b); - } - - /** - * Static factory method for wrapping a value of type C in a {@link CoProduct4}. - * - * @param c 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 - * @param a type parameter representing the fourth possible type of this coproduct - * @return the wrapped value as a CoProduct4<A, B, C, D> - */ - static CoProduct4 c(C c) { - class _C implements CoProduct4 { - - private final C c; - - private _C(C c) { - this.c = c; - } - - @Override - public R match(Function aFn, Function bFn, - Function cFn, Function 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 "CoProduct4{" + - "c=" + c + - '}'; - } - } - return new _C(c); - } - - /** - * Static factory method for wrapping a value of type D in a {@link CoProduct4}. - * - * @param d 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 - * @param a type parameter representing the fourth possible type of this coproduct - * @return the wrapped value as a CoProduct4<A, B, C, D> - */ - static CoProduct4 d(D d) { - class _D implements CoProduct4 { - - private final D d; - - private _D(D d) { - this.d = d; - } - - @Override - public R match(Function aFn, Function bFn, - Function cFn, Function 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 "CoProduct4{" + - "d=" + d + - '}'; - } - } - - return new _D(d); - } } 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 a6d46ab49..9379a3d7c 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct5.java @@ -1,10 +1,7 @@ package com.jnape.palatable.lambda.adt.coproduct; import com.jnape.palatable.lambda.adt.hlist.Tuple5; -import com.jnape.palatable.lambda.functor.Bifunctor; -import com.jnape.palatable.lambda.functor.Functor; -import java.util.Objects; import java.util.Optional; import java.util.function.Function; @@ -22,7 +19,7 @@ * @see CoProduct2 */ @FunctionalInterface -public interface CoProduct5 extends Functor, Bifunctor { +public interface CoProduct5 { /** * Type-safe convergence requiring a match against all potential types. @@ -50,7 +47,31 @@ R match(Function aFn, * @return a CoProduct4<A, B, C, D> */ default CoProduct4 converge(Function> convergenceFn) { - return match(CoProduct4::a, CoProduct4::b, CoProduct4::c, CoProduct4::d, convergenceFn); + return match(a -> new CoProduct4() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn) { + return aFn.apply(a); + } + }, b -> new CoProduct4() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn) { + return bFn.apply(b); + } + }, c -> new CoProduct4() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn) { + return cFn.apply(c); + } + }, d -> new CoProduct4() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn) { + return dFn.apply(d); + } + }, convergenceFn::apply); } /** @@ -72,7 +93,6 @@ default Tuple5, Optional, Optional, Optional, Optional> * * @return an optional value representing the projection of the "a" type index */ - @SuppressWarnings("unused") default Optional projectA() { return project()._1(); } @@ -82,7 +102,6 @@ default Optional projectA() { * * @return an optional value representing the projection of the "b" type index */ - @SuppressWarnings("unused") default Optional projectB() { return project()._2(); } @@ -92,7 +111,6 @@ default Optional projectB() { * * @return an optional value representing the projection of the "c" type index */ - @SuppressWarnings("unused") default Optional projectC() { return project()._3(); } @@ -102,7 +120,6 @@ default Optional projectC() { * * @return an optional value representing the projection of the "d" type index */ - @SuppressWarnings("unused") default Optional projectD() { return project()._4(); } @@ -112,276 +129,7 @@ default Optional projectD() { * * @return an optional value representing the projection of the "e" type index */ - @SuppressWarnings("unused") default Optional projectE() { return project()._5(); } - - @Override - default CoProduct5 fmap(Function fn) { - return biMapR(fn); - } - - @Override - @SuppressWarnings("unchecked") - default CoProduct5 biMapL(Function fn) { - return (CoProduct5) Bifunctor.super.biMapL(fn); - } - - @Override - @SuppressWarnings("unchecked") - default CoProduct5 biMapR(Function fn) { - return (CoProduct5) Bifunctor.super.biMapR(fn); - } - - @Override - default CoProduct5 biMap(Function lFn, - Function rFn) { - return match(CoProduct5::a, CoProduct5::b, CoProduct5::c, d -> d(lFn.apply(d)), e -> e(rFn.apply(e))); - } - - /** - * Static factory method for wrapping a value of type A in a {@link CoProduct5}. - * - * @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 - * @param a type parameter representing the fourth possible type of this coproduct - * @param a type parameter representing the fifth possible type of this coproduct - * @return the wrapped value as a CoProduct5<A, B, C, D, E> - */ - static CoProduct5 a(A a) { - class _A implements CoProduct5 { - - 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) { - 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 "CoProduct5{" + - "a=" + a + - '}'; - } - } - - return new _A(a); - } - - /** - * Static factory method for wrapping a value of type B in a {@link CoProduct5}. - * - * @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 - * @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 - * @return the wrapped value as a CoProduct5<A, B, C, D, E> - */ - static CoProduct5 b(B b) { - class _B implements CoProduct5 { - - 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) { - 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 "CoProduct5{" + - "b=" + b + - '}'; - } - } - - return new _B(b); - } - - /** - * Static factory method for wrapping a value of type C in a {@link CoProduct5}. - * - * @param c 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 - * @param a type parameter representing the fourth possible type of this coproduct - * @param a type parameter representing the fifth possible type of this coproduct - * @return the wrapped value as a CoProduct5<A, B, C, D, E> - */ - static CoProduct5 c(C c) { - class _C implements CoProduct5 { - - 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) { - 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 "CoProduct5{" + - "c=" + c + - '}'; - } - } - - return new _C(c); - } - - /** - * Static factory method for wrapping a value of type D in a {@link CoProduct5}. - * - * @param d 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 - * @param a type parameter representing the fourth possible type of this coproduct - * @param a type parameter representing the fifth possible type of this coproduct - * @return the wrapped value as a CoProduct5<A, B, C, D, E> - */ - static CoProduct5 d(D d) { - class _D implements CoProduct5 { - - 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) { - 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 "CoProduct5{" + - "d=" + d + - '}'; - } - } - - return new _D(d); - } - - /** - * Static factory method for wrapping a value of type E in a {@link CoProduct5}. - * - * @param e 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 - * @param a type parameter representing the fourth possible type of this coproduct - * @param a type parameter representing the fifth possible type of this coproduct - * @return the wrapped value as a CoProduct5<A, B, C, D, E> - */ - static CoProduct5 e(E e) { - class _E implements CoProduct5 { - - 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) { - 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 "CoProduct5{" + - "e=" + e + - '}'; - } - } - - return new _E(e); - } } diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java index a738f7700..95fc2b305 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple2.java @@ -1,11 +1,11 @@ package com.jnape.palatable.lambda.adt.hlist; import com.jnape.palatable.lambda.adt.hlist.HList.HCons; -import com.jnape.palatable.lambda.monoid.Monoid; import com.jnape.palatable.lambda.functor.Bifunctor; import com.jnape.palatable.lambda.functor.Functor; import java.util.Map; +import java.util.function.BiFunction; import java.util.function.Function; /** @@ -53,6 +53,18 @@ public _2 _2() { return _2; } + /** + * Destructure and apply this tuple to a function accepting the same number of arguments as this tuple's + * slots. This can be thought of as a kind of dual to uncurrying a function and applying a tuple to it. + * + * @param fn the function to apply + * @param the return type of the function + * @return the result of applying the destructured tuple to the function + */ + public R into(BiFunction fn) { + return fn.apply(_1, _2); + } + @Override public _1 getKey() { return _1(); @@ -91,13 +103,6 @@ public <_1Prime, _2Prime> Tuple2<_1Prime, _2Prime> biMap(Function(lFn.apply(_1()), tail().fmap(rFn)); } - public static <_1, _2> Monoid> monoid(Monoid<_1> _1Monoid, Monoid<_2> _2Monoid) { - return Monoid.monoid( - (x, y) -> x.biMap(_1Monoid.flip().apply(y._1()), - _2Monoid.flip().apply(y._2())), - tuple(_1Monoid.identity(), _2Monoid.identity())); - } - /** * Static factory method for creating Tuple2s from {@link java.util.Map.Entry}s. * @@ -110,4 +115,14 @@ public static Tuple2 fromEntry(Map.Entry entry) { return new Tuple2<>(entry.getKey(), singletonHList(entry.getValue())); } + /** + * 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 + */ + public static Tuple2 fill(A a) { + return tuple(a, a); + } } diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.java index 431eeb375..aa07008ad 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple3.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.Fn3; import com.jnape.palatable.lambda.functor.Bifunctor; import com.jnape.palatable.lambda.functor.Functor; @@ -62,6 +63,19 @@ public _3 _3() { return _3; } + /** + * 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(Fn3 fn) { + return fn.apply(_1, _2, _3); + } + @Override public <_3Prime> Tuple3<_1, _2, _3Prime> fmap(Function fn) { return biMapR(fn); @@ -84,4 +98,16 @@ public <_2Prime, _3Prime> Tuple3<_1, _2Prime, _3Prime> biMap(Function rFn) { return new Tuple3<>(_1(), tail().biMap(lFn, rFn)); } + + /** + * 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 Tuple3 fill(A a) { + return tuple(a, a, a); + } } diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.java b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.java index 35effe953..fc456c966 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hlist/Tuple4.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.Fn4; import com.jnape.palatable.lambda.functor.Bifunctor; import com.jnape.palatable.lambda.functor.Functor; @@ -74,6 +75,19 @@ public _4 _4() { return _4; } + /** + * 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(Fn4 fn) { + return fn.apply(_1, _2, _3, _4); + } + @Override public <_4Prime> Tuple4<_1, _2, _3, _4Prime> fmap(Function fn) { return biMapR(fn); @@ -96,4 +110,16 @@ public <_3Prime, _4Prime> Tuple4<_1, _2, _3Prime, _4Prime> biMap(Function rFn) { return new Tuple4<>(_1(), tail().biMap(lFn, rFn)); } + + /** + * 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 Tuple4 fill(A a) { + return tuple(a, a, a, a); + } } 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 a138f4d76..101440fdb 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 @@ -108,4 +108,16 @@ public <_4Prime, _5Prime> Tuple5<_1, _2, _3, _4Prime, _5Prime> biMap(Function rFn) { return new Tuple5<>(_1(), tail().biMap(lFn, rFn)); } + + /** + * 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 Tuple5 fill(A a) { + return tuple(a, a, a, a, a); + } } diff --git a/src/main/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKey.java b/src/main/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKey.java index d62178132..576b87d5c 100644 --- a/src/main/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKey.java +++ b/src/main/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKey.java @@ -10,7 +10,6 @@ * * @param The type of the value that this key maps to inside an HMap */ -@SuppressWarnings("unused") public interface TypeSafeKey { /** diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Constantly.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Constantly.java index fb034b3fe..9f89da517 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Constantly.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Constantly.java @@ -11,6 +11,8 @@ */ public final class Constantly implements Fn2 { + private static final Constantly INSTANCE = new Constantly(); + private Constantly() { } @@ -19,8 +21,9 @@ public A apply(A a, B b) { return a; } + @SuppressWarnings("unchecked") public static Constantly constantly() { - return new Constantly<>(); + return INSTANCE; } public static Fn1 constantly(A a) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Cycle.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Cycle.java index 8f626dd33..440d50041 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Cycle.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Cycle.java @@ -13,6 +13,8 @@ */ public final class Cycle implements Fn1, Iterable> { + private static final Cycle INSTANCE = new Cycle(); + private Cycle() { } @@ -21,8 +23,9 @@ public Iterable apply(Iterable as) { return () -> new CyclicIterator<>(as.iterator()); } + @SuppressWarnings("unchecked") public static Cycle cycle() { - return new Cycle<>(); + return INSTANCE; } public static Iterable cycle(Iterable as) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Head.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Head.java index 9cd2c6ab7..2ce501dee 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Head.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Head.java @@ -13,6 +13,8 @@ */ public final class Head implements Fn1, Optional> { + private static final Head INSTANCE = new Head(); + private Head() { } @@ -24,8 +26,9 @@ public Optional apply(Iterable as) { : Optional.empty(); } + @SuppressWarnings("unchecked") public static Head head() { - return new Head<>(); + return INSTANCE; } public static Optional head(Iterable as) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Id.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Id.java index 7bba0f7f7..640927f50 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Id.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Id.java @@ -9,7 +9,7 @@ */ public final class Id implements Fn1 { - private static final Id ID = new Id(); + private static final Id INSTANCE = new Id(); private Id() { } @@ -21,6 +21,6 @@ public A apply(A a) { @SuppressWarnings("unchecked") public static Id id() { - return (Id) ID; + return INSTANCE; } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Last.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Last.java new file mode 100644 index 000000000..4b993c35c --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Last.java @@ -0,0 +1,37 @@ +package com.jnape.palatable.lambda.functions.builtin.fn1; + +import com.jnape.palatable.lambda.functions.Fn1; + +import java.util.Optional; + +/** + * Retrieve the last element of an Iterable, wrapped in an Optional. If the + * Iterable is empty, the result is Optional.empty(). + * + * @param the Iterable element type + */ +public final class Last implements Fn1, Optional> { + + private static final Last INSTANCE = new Last(); + + private Last() { + } + + @Override + public Optional apply(Iterable as) { + A last = null; + for (A a : as) { + last = a; + } + return Optional.ofNullable(last); + } + + @SuppressWarnings("unchecked") + public static Last last() { + return INSTANCE; + } + + public static Optional last(Iterable as) { + return Last.last().apply(as); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Repeat.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Repeat.java index 6e1547155..30da5141f 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Repeat.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Repeat.java @@ -10,6 +10,8 @@ */ public final class Repeat implements Fn1> { + private static final Repeat INSTANCE = new Repeat(); + private Repeat() { } @@ -18,8 +20,9 @@ public Iterable apply(A a) { return () -> new RepetitiousIterator<>(a); } + @SuppressWarnings("unchecked") public static Repeat repeat() { - return new Repeat<>(); + return INSTANCE; } public static Iterable repeat(A a) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Reverse.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Reverse.java index 51637ce0d..f2ab76ae6 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Reverse.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Reverse.java @@ -11,6 +11,8 @@ */ public final class Reverse implements Fn1, Iterable> { + private static final Reverse INSTANCE = new Reverse(); + private Reverse() { } @@ -19,8 +21,9 @@ public Iterable apply(Iterable as) { return () -> new ReversingIterator<>(as.iterator()); } + @SuppressWarnings("unchecked") public static Reverse reverse() { - return new Reverse<>(); + return INSTANCE; } public static Iterable reverse(Iterable as) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Tail.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Tail.java index 87f8dd57f..e155134fc 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Tail.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/Tail.java @@ -12,6 +12,8 @@ */ public final class Tail implements Fn1, Iterable> { + private static final Tail INSTANCE = new Tail(); + private Tail() { } @@ -25,8 +27,9 @@ public Iterable apply(Iterable as) { }; } + @SuppressWarnings("unchecked") public static Tail tail() { - return new Tail<>(); + return INSTANCE; } public static Iterable tail(Iterable as) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/All.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/All.java index 879aca6a4..53ecac095 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/All.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/All.java @@ -15,6 +15,8 @@ */ public final class All implements BiPredicate, Iterable> { + private static final All INSTANCE = new All(); + private All() { } @@ -27,8 +29,9 @@ public Boolean apply(Function predicate, Iterable as) { return true; } + @SuppressWarnings("unchecked") public static All all() { - return new All<>(); + return INSTANCE; } public static Fn1, Boolean> all(Function predicate) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Any.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Any.java index 93debeaa7..09917f2a3 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Any.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Any.java @@ -1,7 +1,6 @@ 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.specialized.BiPredicate; import java.util.function.Function; @@ -16,6 +15,8 @@ */ public final class Any implements BiPredicate, Iterable> { + private static final Any INSTANCE = new Any(); + private Any() { } @@ -28,8 +29,9 @@ public Boolean apply(Function predicate, Iterable as) { return false; } + @SuppressWarnings("unchecked") public static Any any() { - return new Any<>(); + return INSTANCE; } public static Fn1, Boolean> any(Function predicate) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/CartesianProduct.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/CartesianProduct.java index 40da00035..ec22bb0ed 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/CartesianProduct.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/CartesianProduct.java @@ -21,6 +21,8 @@ */ public final class CartesianProduct implements Fn2, Iterable, Iterable>> { + private static final CartesianProduct INSTANCE = new CartesianProduct(); + private CartesianProduct() { } @@ -29,8 +31,9 @@ public Iterable> apply(Iterable as, Iterable bs) { return () -> new CombinatorialIterator<>(as.iterator(), bs.iterator()); } + @SuppressWarnings("unchecked") public static CartesianProduct cartesianProduct() { - return new CartesianProduct<>(); + return INSTANCE; } public static Fn1, Iterable>> cartesianProduct(Iterable as) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Cons.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Cons.java new file mode 100644 index 000000000..a4e146cae --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Cons.java @@ -0,0 +1,36 @@ +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.iterators.ConsingIterator; + +/** + * Prepend an element to an Iterable. + * + * @param the Iterable element type + */ +public final class Cons implements Fn2, Iterable> { + + private static final Cons INSTANCE = new Cons(); + + private Cons() { + } + + @Override + public Iterable apply(A a, Iterable as) { + return () -> new ConsingIterator<>(a, as); + } + + @SuppressWarnings("unchecked") + public static Cons cons() { + return (Cons) INSTANCE; + } + + public static Fn1, Iterable> cons(A a) { + return Cons.cons().apply(a); + } + + public static Iterable cons(A a, Iterable as) { + return cons(a).apply(as); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Drop.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Drop.java index 4a25573d3..a0c1d9082 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Drop.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Drop.java @@ -15,6 +15,8 @@ */ public final class Drop implements Fn2, Iterable> { + private static final Drop INSTANCE = new Drop(); + private Drop() { } @@ -23,8 +25,9 @@ public Iterable apply(Integer n, Iterable as) { return () -> new DroppingIterator<>(n, as.iterator()); } + @SuppressWarnings("unchecked") public static Drop drop() { - return new Drop<>(); + return INSTANCE; } public static Fn1, Iterable> drop(int n) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/DropWhile.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/DropWhile.java index ceabf8241..a11472d68 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/DropWhile.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/DropWhile.java @@ -18,6 +18,8 @@ public final class DropWhile implements Fn2, Iterable, Iterable> { + private static final DropWhile INSTANCE = new DropWhile(); + private DropWhile() { } @@ -26,8 +28,9 @@ public Iterable apply(Function predicate, Iterable as) return () -> new PredicatedDroppingIterator<>(predicate, as.iterator()); } + @SuppressWarnings("unchecked") public static DropWhile dropWhile() { - return new DropWhile<>(); + return INSTANCE; } public static Fn1, Iterable> dropWhile(Function predicate) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Eq.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Eq.java index b0f7bd9a0..83cce842c 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Eq.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Eq.java @@ -10,6 +10,8 @@ */ public final class Eq implements BiPredicate { + private static final Eq INSTANCE = new Eq(); + private Eq() { } @@ -18,8 +20,9 @@ public Boolean apply(A x, A y) { return x == null ? y == null : x.equals(y); } + @SuppressWarnings("unchecked") public static Eq eq() { - return new Eq<>(); + return INSTANCE; } public static Predicate eq(A x) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Filter.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Filter.java index eb0159883..ebad91eb5 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Filter.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Filter.java @@ -16,6 +16,8 @@ */ public final class Filter implements Fn2, Iterable, Iterable> { + private static final Filter INSTANCE = new Filter(); + private Filter() { } @@ -24,8 +26,9 @@ public Iterable apply(Function predicate, Iterable as) return () -> new FilteringIterator<>(predicate, as.iterator()); } + @SuppressWarnings("unchecked") public static Filter filter() { - return new Filter<>(); + return INSTANCE; } public static Fn1, Iterable> filter(Function predicate) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Find.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Find.java index 88c9a33c1..583f29d20 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Find.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Find.java @@ -20,6 +20,8 @@ */ public final class Find implements Fn2, Iterable, Optional> { + private static final Find INSTANCE = new Find(); + private Find() { } @@ -28,8 +30,9 @@ public Optional apply(Function predicate, Iterable as) return head(dropWhile(((Predicate) predicate::apply).negate(), as)); } + @SuppressWarnings("unchecked") public static Find find() { - return new Find<>(); + return INSTANCE; } public static Fn1, Optional> find(Function predicate) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/InGroupsOf.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/InGroupsOf.java index f990ecab2..ebdf7cab7 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/InGroupsOf.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/InGroupsOf.java @@ -14,6 +14,8 @@ */ public final class InGroupsOf implements Fn2, Iterable>> { + private static final InGroupsOf INSTANCE = new InGroupsOf(); + private InGroupsOf() { } @@ -22,8 +24,9 @@ public Iterable> apply(Integer k, Iterable as) { return () -> new GroupingIterator<>(k, as.iterator()); } + @SuppressWarnings("unchecked") public static InGroupsOf inGroupsOf() { - return new InGroupsOf<>(); + return INSTANCE; } public static Fn1, Iterable>> inGroupsOf(Integer k) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Intersperse.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Intersperse.java new file mode 100644 index 000000000..d7f0e3b63 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Intersperse.java @@ -0,0 +1,33 @@ +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.Tail.tail; +import static com.jnape.palatable.lambda.functions.builtin.fn2.PrependAll.prependAll; + +public final class Intersperse implements Fn2, Iterable> { + + private static final Intersperse INSTANCE = new Intersperse(); + + private Intersperse() { + } + + @Override + public Iterable apply(A a, Iterable as) { + return tail(prependAll(a, as)); + } + + @SuppressWarnings("unchecked") + public static Intersperse intersperse() { + return INSTANCE; + } + + public static Fn1, Iterable> intersperse(A a) { + return Intersperse.intersperse().apply(a); + } + + public static Iterable intersperse(A a, Iterable as) { + return intersperse(a).apply(as); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Iterate.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Iterate.java index afc132cd8..18c299abe 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Iterate.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Iterate.java @@ -18,6 +18,8 @@ */ public final class Iterate implements Fn2, A, Iterable> { + private static final Iterate INSTANCE = new Iterate(); + private Iterate() { } @@ -26,8 +28,9 @@ public Iterable apply(Function fn, A seed) { return unfoldr(a -> Optional.of(tuple(a, fn.apply(a))), seed); } + @SuppressWarnings("unchecked") public static Iterate iterate() { - return new Iterate<>(); + return INSTANCE; } public static Fn1> iterate(Function fn) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Map.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Map.java index 9805d54af..1736edb0f 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Map.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Map.java @@ -15,6 +15,8 @@ */ public final class Map implements Fn2, Iterable, Iterable> { + private static final Map INSTANCE = new Map(); + private Map() { } @@ -23,8 +25,9 @@ public Iterable apply(Function fn, Iterable as) { return () -> new MappingIterator<>(fn, as.iterator()); } + @SuppressWarnings("unchecked") public static Map map() { - return new Map<>(); + return INSTANCE; } public static Fn1, Iterable> map(Function fn) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Partial2.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Partial2.java index f2895b906..2c2317470 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Partial2.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Partial2.java @@ -16,6 +16,8 @@ */ public final class Partial2 implements Fn2, A, Fn1> { + private static final Partial2 INSTANCE = new Partial2(); + private Partial2() { } @@ -24,8 +26,9 @@ public Fn1 apply(BiFunction fn, A a) { return b -> fn.apply(a, b); } + @SuppressWarnings("unchecked") public static Partial2, A, Fn1> partial2() { - return new Partial2<>(); + return INSTANCE; } public static Fn1> partial2(BiFunction fn) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Partial3.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Partial3.java index 8cf530b6e..8473a356b 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Partial3.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Partial3.java @@ -16,6 +16,8 @@ */ public final class Partial3 implements Fn2, A, Fn2> { + private static final Partial3 INSTANCE = new Partial3(); + private Partial3() { } @@ -24,8 +26,9 @@ public Fn2 apply(Fn3 fn, A a) { return fn.apply(a); } + @SuppressWarnings("unchecked") public static Partial3 partial3() { - return new Partial3<>(); + return INSTANCE; } public static Fn1> partial3(Fn3 fn) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Partition.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Partition.java index 57056430b..90f142288 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Partition.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Partition.java @@ -26,6 +26,8 @@ */ public final class Partition implements Fn2>, Iterable, Tuple2, Iterable>> { + private static final Partition INSTANCE = new Partition(); + private Partition() { } @@ -44,8 +46,9 @@ private Iterable unwrapRight(Iterable> eithers) { return map(Optional::get, filter(Optional::isPresent, map(Either::toOptional, eithers))); } + @SuppressWarnings("unchecked") public static Partition partition() { - return new Partition<>(); + return INSTANCE; } public static Fn1, Tuple2, Iterable>> partition( diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/PrependAll.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/PrependAll.java new file mode 100644 index 000000000..d29ae7c6c --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/PrependAll.java @@ -0,0 +1,35 @@ +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.Head.head; +import static com.jnape.palatable.lambda.functions.builtin.fn1.Tail.tail; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Cons.cons; +import static java.util.Collections.emptyList; + +public final class PrependAll implements Fn2, Iterable> { + + private static final PrependAll INSTANCE = new PrependAll(); + + private PrependAll() { + } + + @Override + public Iterable apply(A a, Iterable as) { + return () -> head(as).map(head -> cons(a, cons(head, prependAll(a, tail(as))))).orElse(emptyList()).iterator(); + } + + @SuppressWarnings("unchecked") + public static PrependAll prependAll() { + return INSTANCE; + } + + public static Fn1, Iterable> prependAll(A a) { + return PrependAll.prependAll().apply(a); + } + + public static Iterable prependAll(A a, Iterable as) { + return prependAll(a).apply(as); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ReduceLeft.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ReduceLeft.java index e8f595576..d333ba2e9 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ReduceLeft.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ReduceLeft.java @@ -25,6 +25,8 @@ */ public final class ReduceLeft implements Fn2, Iterable, Optional> { + private static final ReduceLeft INSTANCE = new ReduceLeft(); + private ReduceLeft() { } @@ -37,8 +39,9 @@ public Optional apply(BiFunction fn, Itera return Optional.of(foldLeft(fn, iterator.next(), () -> iterator)); } + @SuppressWarnings("unchecked") public static ReduceLeft reduceLeft() { - return new ReduceLeft<>(); + return INSTANCE; } public static Fn1, Optional> reduceLeft(BiFunction fn) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ReduceRight.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ReduceRight.java index b12efe118..4e2181024 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ReduceRight.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ReduceRight.java @@ -25,6 +25,8 @@ */ public final class ReduceRight implements Fn2, Iterable, Optional> { + private static final ReduceRight INSTANCE = new ReduceRight(); + private ReduceRight() { } @@ -33,8 +35,9 @@ public final Optional apply(BiFunction fn, return reduceLeft((b, a) -> fn.apply(a, b), reverse(as)); } + @SuppressWarnings("unchecked") public static ReduceRight reduceRight() { - return new ReduceRight<>(); + return INSTANCE; } public static Fn1, Optional> reduceRight(BiFunction fn) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Take.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Take.java index 2d095724c..a67bdcfd4 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Take.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Take.java @@ -15,6 +15,8 @@ */ public final class Take implements Fn2, Iterable> { + private static final Take INSTANCE = new Take(); + private Take() { } @@ -23,8 +25,9 @@ public Iterable apply(Integer n, Iterable as) { return () -> new TakingIterator<>(n, as.iterator()); } + @SuppressWarnings("unchecked") public static Take take() { - return new Take<>(); + return INSTANCE; } public static Fn1, Iterable> take(int n) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/TakeWhile.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/TakeWhile.java index 595e5112b..e14f5caf8 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/TakeWhile.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/TakeWhile.java @@ -17,6 +17,8 @@ */ public final class TakeWhile implements Fn2, Iterable, Iterable> { + private static final TakeWhile INSTANCE = new TakeWhile(); + private TakeWhile() { } @@ -25,8 +27,9 @@ public Iterable apply(Function predicate, Iterable as) return () -> new PredicatedTakingIterator<>(predicate, as.iterator()); } + @SuppressWarnings("unchecked") public static TakeWhile takeWhile() { - return new TakeWhile<>(); + return INSTANCE; } public static Fn1, Iterable> takeWhile(Function predicate) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/ToCollection.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToCollection.java similarity index 96% rename from src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/ToCollection.java rename to src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToCollection.java index 6f5f803ff..b9b46b893 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn1/ToCollection.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToCollection.java @@ -1,4 +1,4 @@ -package com.jnape.palatable.lambda.functions.builtin.fn1; +package com.jnape.palatable.lambda.functions.builtin.fn2; import com.jnape.palatable.lambda.functions.Fn1; import com.jnape.palatable.lambda.functions.Fn2; diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToMap.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToMap.java new file mode 100644 index 000000000..7db0b82ab --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToMap.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 java.util.Map; +import java.util.function.Supplier; + +/** + * Given a {@link Supplier} of some {@link Map} M, create an instance of M and put + * all of the entries in the provided Iterable into the instance. Note that instances of M + * must support {@link java.util.Map#put} (which is to say, must not throw on invocation). + * + * @param the key element type + * @param the value element type + * @param the resulting map type + */ +public final class ToMap> implements Fn2, Iterable>, M> { + + private static final ToMap INSTANCE = new ToMap<>(); + + private ToMap() { + } + + @Override + public M apply(Supplier mSupplier, Iterable> entries) { + M m = mSupplier.get(); + entries.forEach(kv -> m.put(kv.getKey(), kv.getValue())); + return m; + } + + @SuppressWarnings("unchecked") + public static > ToMap toMap() { + return INSTANCE; + } + + public static > Fn1>, M> toMap(Supplier mSupplier) { + return ToMap.toMap().apply(mSupplier); + } + + public static > M toMap(Supplier mSupplier, Iterable> entries) { + return toMap(mSupplier).apply(entries); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Tupler2.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Tupler2.java index aed51e164..de1b4a283 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Tupler2.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Tupler2.java @@ -15,6 +15,8 @@ */ public final class Tupler2 implements Fn2> { + private static final Tupler2 INSTANCE = new Tupler2(); + private Tupler2() { } @@ -23,8 +25,9 @@ public Tuple2 apply(A a, B b) { return tuple(a, b); } + @SuppressWarnings("unchecked") public static Tupler2 tupler() { - return new Tupler2<>(); + return INSTANCE; } public static Fn1> tupler(A a) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Unfoldr.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Unfoldr.java index 3fc3cb2b6..196d743a6 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Unfoldr.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Unfoldr.java @@ -31,6 +31,8 @@ */ public final class Unfoldr implements Fn2>>, B, Iterable> { + private static final Unfoldr INSTANCE = new Unfoldr(); + private Unfoldr() { } @@ -39,8 +41,9 @@ public Iterable apply(Function>> fn, B b) { return () -> new UnfoldingIterator<>(fn, b); } + @SuppressWarnings("unchecked") public static Unfoldr unfoldr() { - return new Unfoldr<>(); + return INSTANCE; } public static Fn1> unfoldr(Function>> fn) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Zip.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Zip.java index b9ae36cf6..323f00e02 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Zip.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn2/Zip.java @@ -17,6 +17,8 @@ */ public final class Zip implements Fn2, Iterable, Iterable>> { + private static final Zip INSTANCE = new Zip(); + private Zip() { } @@ -25,8 +27,9 @@ public Iterable> apply(Iterable as, Iterable bs) { return zipWith(Tupler2.tupler().toBiFunction(), as, bs); } + @SuppressWarnings("unchecked") public static Zip zip() { - return new Zip<>(); + return INSTANCE; } public static Fn1, Iterable>> zip(Iterable as) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/FoldLeft.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/FoldLeft.java index a64e099ae..17fb496d1 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/FoldLeft.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/FoldLeft.java @@ -22,6 +22,8 @@ */ public final class FoldLeft implements Fn3, B, Iterable, B> { + private static final FoldLeft INSTANCE = new FoldLeft(); + private FoldLeft() { } @@ -33,8 +35,9 @@ public B apply(BiFunction fn, B acc, Iterable return accumulation; } + @SuppressWarnings("unchecked") public static FoldLeft foldLeft() { - return new FoldLeft<>(); + return INSTANCE; } public static Fn2, B> foldLeft(BiFunction fn) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/FoldRight.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/FoldRight.java index 545ee5eb6..bb6f49f2c 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/FoldRight.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/FoldRight.java @@ -25,6 +25,8 @@ */ public final class FoldRight implements Fn3, B, Iterable, B> { + private static final FoldRight INSTANCE = new FoldRight(); + private FoldRight() { } @@ -33,8 +35,9 @@ public B apply(BiFunction fn, B acc, Iterable return foldLeft((b, a) -> fn.apply(a, b), acc, reverse(as)); } + @SuppressWarnings("unchecked") public static FoldRight foldRight() { - return new FoldRight<>(); + return INSTANCE; } public static Fn2, B> foldRight(BiFunction fn) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/ScanLeft.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/ScanLeft.java index 5dcb2a309..a6af6210b 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/ScanLeft.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/ScanLeft.java @@ -20,6 +20,8 @@ */ public final class ScanLeft implements Fn3, B, Iterable, Iterable> { + private static final ScanLeft INSTANCE = new ScanLeft(); + private ScanLeft() { } @@ -28,8 +30,9 @@ public Iterable apply(BiFunction fn, B b, return () -> new ScanningIterator<>(fn, b, as.iterator()); } + @SuppressWarnings("unchecked") public static ScanLeft scanLeft() { - return new ScanLeft<>(); + return INSTANCE; } public static Fn2, Iterable> scanLeft(BiFunction fn) { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/ZipWith.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/ZipWith.java index 5692295f8..28a83fdeb 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/ZipWith.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/fn3/ZipWith.java @@ -19,6 +19,8 @@ */ public final class ZipWith implements Fn3, Iterable, Iterable, Iterable> { + private static final ZipWith INSTANCE = new ZipWith(); + private ZipWith() { } @@ -27,8 +29,9 @@ public Iterable apply(BiFunction zipper, I return () -> new ZippingIterator<>(zipper, as.iterator(), bs.iterator()); } + @SuppressWarnings("unchecked") public static ZipWith zipWith() { - return new ZipWith<>(); + return INSTANCE; } public static Fn2, Iterable, Iterable> zipWith( diff --git a/src/main/java/com/jnape/palatable/lambda/functions/specialized/checked/CheckedSupplier.java b/src/main/java/com/jnape/palatable/lambda/functions/specialized/checked/CheckedSupplier.java index b16b2d6f8..dd38fc165 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/specialized/checked/CheckedSupplier.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/specialized/checked/CheckedSupplier.java @@ -12,7 +12,7 @@ * @see CheckedFn1 */ @FunctionalInterface -public interface CheckedSupplier extends Supplier { +public interface CheckedSupplier extends Supplier { @Override default T get() { diff --git a/src/main/java/com/jnape/palatable/lambda/iterators/ConsingIterator.java b/src/main/java/com/jnape/palatable/lambda/iterators/ConsingIterator.java new file mode 100644 index 000000000..c8dee5b3b --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/iterators/ConsingIterator.java @@ -0,0 +1,49 @@ +package com.jnape.palatable.lambda.iterators; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.function.Supplier; + +public final class ConsingIterator implements Iterator { + + private final A head; + private final Supplier> asSupplier; + private Iterator asIterator; + private boolean iteratedHead; + + public ConsingIterator(A head, Iterable as) { + this.head = head; + this.asSupplier = as::iterator; + iteratedHead = false; + } + + @Override + public boolean hasNext() { + if (!iteratedHead) + return true; + + if (asIterator == null) + asIterator = asSupplier.get(); + + return asIterator.hasNext(); + } + + @Override + public A next() { + if (!hasNext()) + throw new NoSuchElementException(); + + if (!iteratedHead) { + iteratedHead = true; + return head; + } + + while (asIterator instanceof ConsingIterator && ((ConsingIterator) asIterator).iteratedHead) { + ConsingIterator cons = (ConsingIterator) asIterator; + if (cons.iteratedHead && cons.asIterator != null) + asIterator = cons.asIterator; + } + + return asIterator.next(); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/lens/functions/Over.java b/src/main/java/com/jnape/palatable/lambda/lens/functions/Over.java index 782f3d5c7..e9c4aadd6 100644 --- a/src/main/java/com/jnape/palatable/lambda/lens/functions/Over.java +++ b/src/main/java/com/jnape/palatable/lambda/lens/functions/Over.java @@ -25,6 +25,8 @@ */ public final class Over implements Fn3, Function, S, T> { + private static final Over INSTANCE = new Over(); + private Over() { } @@ -35,8 +37,9 @@ public T apply(Lens lens, Function fn, S s) .runIdentity(); } + @SuppressWarnings("unchecked") public static Over over() { - return new Over<>(); + return (Over) INSTANCE; } public static Fn2, S, T> over( diff --git a/src/main/java/com/jnape/palatable/lambda/lens/functions/Set.java b/src/main/java/com/jnape/palatable/lambda/lens/functions/Set.java index aed093e8f..b3c5ff243 100644 --- a/src/main/java/com/jnape/palatable/lambda/lens/functions/Set.java +++ b/src/main/java/com/jnape/palatable/lambda/lens/functions/Set.java @@ -25,6 +25,8 @@ */ public final class Set implements Fn3, B, S, T> { + private static final Set INSTANCE = new Set(); + private Set() { } @@ -33,8 +35,9 @@ public T apply(Lens lens, B b, S s) { return over(lens, constantly(b), s); } + @SuppressWarnings("unchecked") public static Set set() { - return new Set<>(); + return INSTANCE; } public static Fn2 set(Lens lens) { diff --git a/src/main/java/com/jnape/palatable/lambda/lens/functions/View.java b/src/main/java/com/jnape/palatable/lambda/lens/functions/View.java index 966debeff..8cab0c403 100644 --- a/src/main/java/com/jnape/palatable/lambda/lens/functions/View.java +++ b/src/main/java/com/jnape/palatable/lambda/lens/functions/View.java @@ -20,6 +20,8 @@ */ public final class View implements Fn2, S, A> { + private static final View INSTANCE = new View(); + private View() { } @@ -28,8 +30,9 @@ public A apply(Lens lens, S s) { return lens., Const>fix().apply(Const::new, s).runConst(); } + @SuppressWarnings("unchecked") public static View view() { - return new View<>(); + return INSTANCE; } public static Fn1 view(Lens lens) { 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 b51e5ebb5..8cb9ff4dc 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 @@ -1,5 +1,6 @@ package com.jnape.palatable.lambda.lens.lenses; +import com.jnape.palatable.lambda.functions.builtin.fn2.Filter; import com.jnape.palatable.lambda.lens.Lens; import java.util.Collection; @@ -8,11 +9,16 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Function; +import static com.jnape.palatable.lambda.functions.builtin.fn2.Eq.eq; +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.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.OptionalLens.unLiftA; +import static java.util.stream.Collectors.toMap; import static java.util.stream.Collectors.toSet; /** @@ -24,8 +30,7 @@ private MapLens() { } /** - * Convenience static factory method for creating a lens that focuses on a copy of a Map. Useful for composition to - * avoid mutating a map reference. + * A lens that focuses on a copy of a Map. Useful for composition to avoid mutating a map reference. * * @param the key type * @param the value type @@ -36,8 +41,7 @@ public static Lens.Simple, Map> asCopy() { } /** - * Convenience static factory method for creating a lens that focuses on a value at a key in a map, as an {@link - * Optional}. + * A lens that focuses on a value at a key in a map, as an {@link Optional}. * * @param k the key to focus on * @param the key type @@ -52,8 +56,7 @@ public static Lens, Map, Optional, V> valueAt(K k) { } /** - * Convenience static factory method for creating a lens that focuses on a value at a key in a map, falling back to - * defaultV if the value is missing. + * A lens that focuses on a value at a key in a map, falling back to defaultV if the value is missing. * * @param k the key to focus on * @param defaultValue the default value to use in case of a missing value at key @@ -67,7 +70,7 @@ public static Lens.Simple, V> valueAt(K k, V defaultValue) { } /** - * Convenience static factory method for creating a lens that focuses on the keys of a map. + * A lens that focuses on the keys of a map. * * @param the key type * @param the value type @@ -84,15 +87,15 @@ public static Lens.Simple, Set> keys() { } /** - * Convenience static factory method for creating 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. + * 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. * * @param the key type * @param the value type * @return a lens that focuses on the values of a map */ - public static Lens, Map, Collection, Collection> values() { - return lens(Map::values, (m, vs) -> { + public static Lens.Simple, Collection> values() { + return simpleLens(Map::values, (m, vs) -> { Set valueSet = new HashSet<>(vs); Set matchingKeys = m.entrySet().stream() .filter(kv -> valueSet.contains(kv.getValue())) @@ -104,8 +107,8 @@ public static Lens, Map, Collection, Collection> va } /** - * Convenience static factory method for creating a lens that focuses on the inverse of a map (keys and values - * swapped). In the case of multiple equal values becoming keys, the last one wins. + * A lens that focuses on the inverse of a map (keys and values swapped). In the case of multiple equal values + * becoming keys, the last one wins. * * @param the key type * @param the value type @@ -122,4 +125,27 @@ public static Lens.Simple, Map> inverted() { return m; }); } + + /** + * A lens that focuses on a map while mapping its values with the mapping function. + * + * @param fn the mapping function + * @param the key type + * @param the unfocused map value type + * @param the focused map value type + * @return a lens that focuses on a map while mapping its values + */ + public static Lens.Simple, Map> mappingValues(Function fn) { + return Lens.simpleLens(m -> m.entrySet().stream().collect(toMap(Map.Entry::getKey, kv -> fn.apply(kv.getValue()))), + (s, b) -> { + //todo: remove this madness upon arrival of either invertible functions or Iso + Set retainKeys = Filter.>filter(kv -> eq(fn.apply(kv.getValue()), b.get(kv.getKey()))) + .andThen(map(Map.Entry::getKey)) + .andThen(toCollection(HashSet::new)) + .apply(s.entrySet()); + Map copy = new HashMap<>(s); + copy.keySet().retainAll(retainKeys); + return copy; + }); + } } diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/And.java b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/And.java new file mode 100644 index 000000000..cd10613df --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/And.java @@ -0,0 +1,40 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.monoid.Monoid; + +/** + * A {@link Monoid} instance formed by Boolean. Equivalent to logical &&. + * + * @see Or + * @see Monoid + */ +public class And implements Monoid { + + private static final And INSTANCE = new And(); + + private And() { + } + + @Override + public Boolean identity() { + return true; + } + + @Override + public Boolean apply(Boolean x, Boolean y) { + return x && y; + } + + public static And and() { + return INSTANCE; + } + + public static Fn1 and(Boolean x) { + return and().apply(x); + } + + public static Boolean and(Boolean x, Boolean y) { + return and(x).apply(y); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/First.java b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/First.java new file mode 100644 index 000000000..d9ee1acd0 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/First.java @@ -0,0 +1,47 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.monoid.Monoid; + +import java.util.Optional; + +/** + * A {@link Monoid} instance formed by {@link Optional}<A>. The application to two {@link Optional} + * values produces the first non-empty value, or Optional.empty() if all values are empty. + * + * @param the Optional value parameter type + * @see Last + * @see Present + * @see Monoid + * @see Optional + */ +public final class First implements Monoid> { + + private static final First INSTANCE = new First(); + + private First() { + } + + @Override + public Optional identity() { + return Optional.empty(); + } + + @Override + public Optional apply(Optional x, Optional y) { + return x.map(Optional::of).orElse(y); + } + + @SuppressWarnings("unchecked") + public static First first() { + return INSTANCE; + } + + public static Fn1, Optional> first(Optional x) { + return First.first().apply(x); + } + + public static Optional first(Optional x, Optional y) { + return first(x).apply(y); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Join.java b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Join.java new file mode 100644 index 000000000..9f3713283 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Join.java @@ -0,0 +1,39 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.monoid.Monoid; + +/** + * A {@link Monoid} instance formed by String that concats two strings together. + * + * @see Monoid + */ +public final class Join implements Monoid { + + private static final Join INSTANCE = new Join(); + + private Join() { + } + + @Override + public String identity() { + return ""; + } + + @Override + public String apply(String x, String y) { + return x + y; + } + + public static Join join() { + return INSTANCE; + } + + public static Fn1 join(String x) { + return join().apply(x); + } + + public static String join(String x, String y) { + return join(x).apply(y); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Last.java b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Last.java new file mode 100644 index 000000000..90b80f329 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Last.java @@ -0,0 +1,46 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.monoid.Monoid; + +import java.util.Optional; + +/** + * A {@link Monoid} instance formed by {@link Optional}<A>. The application to two {@link Optional} + * values produces the last non-empty value, or Optional.empty() if all values are empty. + * + * @param the Optional value parameter type + * @see First + * @see Present + * @see Monoid + * @see Optional + */ +public final class Last implements Monoid> { + private static final Last INSTANCE = new Last(); + + private Last() { + } + + @Override + public Optional identity() { + return Optional.empty(); + } + + @Override + public Optional apply(Optional x, Optional y) { + return y.map(Optional::of).orElse(x); + } + + @SuppressWarnings("unchecked") + public static Last last() { + return INSTANCE; + } + + public static Fn1, Optional> last(Optional x) { + return Last.last().apply(x); + } + + public static Optional last(Optional x, Optional y) { + return last(x).apply(y); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Or.java b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Or.java new file mode 100644 index 000000000..55d1611d5 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/monoid/builtin/Or.java @@ -0,0 +1,40 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.lambda.monoid.Monoid; + +/** + * A {@link Monoid} instance formed by Boolean. Equivalent to logical ||. + * + * @see And + * @see Monoid + */ +public class Or implements Monoid { + + private static final Or INSTANCE = new Or(); + + private Or() { + } + + @Override + public Boolean identity() { + return false; + } + + @Override + public Boolean apply(Boolean x, Boolean y) { + return x || y; + } + + public static Or or() { + return INSTANCE; + } + + public static Fn1 or(Boolean x) { + return or().apply(x); + } + + public static Boolean or(Boolean x, Boolean y) { + return or(x).apply(y); + } +} 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 ff5a36684..165d9b019 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java @@ -141,6 +141,15 @@ public void fromOptionalMapsOptionalToEither() { assertThat(fromOptional(absent, () -> "fail"), is(left("fail"))); } + @Test + public void fromOptionalDoesNotEvaluateLeftFnForRight() { + Optional present = Optional.of(1); + AtomicInteger atomicInteger = new AtomicInteger(0); + fromOptional(present, atomicInteger::incrementAndGet); + + assertThat(atomicInteger.get(), is(0)); + } + @Test public void functorProperties() { Either left = left("foo"); diff --git a/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice2Test.java b/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice2Test.java new file mode 100644 index 000000000..d7597d9ab --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice2Test.java @@ -0,0 +1,38 @@ +package com.jnape.palatable.lambda.adt.choice; + +import org.junit.Before; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.choice.Choice2.a; +import static com.jnape.palatable.lambda.adt.choice.Choice2.b; +import static org.junit.Assert.assertEquals; + +public class Choice2Test { + + private Choice2 a; + private Choice2 b; + + @Before + public void setUp() { + a = a(1); + b = b(true); + } + + @Test + public void divergeStaysInChoice() { + assertEquals(Choice3.a(1), a.diverge()); + assertEquals(Choice3.b(true), b.diverge()); + } + + @Test + public void functorProperties() { + assertEquals(a, a.fmap(bool -> !bool)); + assertEquals(b(false), b.fmap(bool -> !bool)); + } + + @Test + public void bifunctorProperties() { + assertEquals(a(-1), a.biMap(i -> i * -1, bool -> !bool)); + assertEquals(b(false), b.biMap(i -> i * -1, bool -> !bool)); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice3Test.java b/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice3Test.java new file mode 100644 index 000000000..0b4b0a9f8 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice3Test.java @@ -0,0 +1,50 @@ +package com.jnape.palatable.lambda.adt.choice; + +import org.junit.Before; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.choice.Choice3.b; +import static com.jnape.palatable.lambda.adt.choice.Choice3.c; +import static org.junit.Assert.assertEquals; + +public class Choice3Test { + + private Choice3 a; + private Choice3 b; + private Choice3 c; + + @Before + public void setUp() { + a = Choice3.a(1); + b = Choice3.b("two"); + c = Choice3.c(true); + } + + @Test + public void convergeStaysInChoice() { + assertEquals(Choice2.a(1), a.converge(c -> Choice2.b(c.toString()))); + assertEquals(Choice2.b("two"), b.converge(c -> Choice2.b(c.toString()))); + assertEquals(Choice2.b("true"), c.converge(c -> Choice2.b(c.toString()))); + } + + @Test + public void divergeStaysInChoice() { + assertEquals(Choice4.a(1), a.diverge()); + assertEquals(Choice4.b("two"), b.diverge()); + assertEquals(Choice4.c(true), c.diverge()); + } + + @Test + public void functorProperties() { + assertEquals(a, a.fmap(bool -> !bool)); + assertEquals(b, b.fmap(bool -> !bool)); + assertEquals(c(false), c.fmap(bool -> !bool)); + } + + @Test + public void bifunctorProperties() { + assertEquals(a, a.biMap(String::toUpperCase, bool -> !bool)); + assertEquals(b("TWO"), b.biMap(String::toUpperCase, bool -> !bool)); + assertEquals(c(false), c.biMap(String::toUpperCase, bool -> !bool)); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice4Test.java b/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice4Test.java new file mode 100644 index 000000000..5be43ca7c --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice4Test.java @@ -0,0 +1,58 @@ +package com.jnape.palatable.lambda.adt.choice; + +import org.junit.Before; +import org.junit.Test; + +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 org.junit.Assert.assertEquals; + +public class Choice4Test { + + private Choice4 a; + private Choice4 b; + private Choice4 c; + private Choice4 d; + + @Before + public void setUp() { + a = a(1); + b = b("two"); + c = c(true); + d = d(4D); + } + + @Test + public void convergeStaysInChoice() { + assertEquals(Choice3.a(1), a.converge(d -> Choice3.b(d.toString()))); + assertEquals(Choice3.b("two"), b.converge(d -> Choice3.b(d.toString()))); + assertEquals(Choice3.c(true), c.converge(d -> Choice3.b(d.toString()))); + assertEquals(Choice3.b("4.0"), d.converge(d -> Choice3.b(d.toString()))); + } + + @Test + public void divergeStaysInChoice() { + assertEquals(Choice5.a(1), a.diverge()); + assertEquals(Choice5.b("two"), b.diverge()); + assertEquals(Choice5.c(true), c.diverge()); + assertEquals(Choice5.d(4D), d.diverge()); + } + + @Test + public void functorProperties() { + assertEquals(a, a.fmap(d -> -d)); + assertEquals(b, b.fmap(d -> -d)); + assertEquals(c, c.fmap(d -> -d)); + assertEquals(d(-4D), d.fmap(d -> -d)); + } + + @Test + public void bifunctorProperties() { + assertEquals(a, a.biMap(c -> !c, d -> -d)); + assertEquals(b, b.biMap(c -> !c, d -> -d)); + assertEquals(c(false), c.biMap(c -> !c, d -> -d)); + assertEquals(d(-4D), d.biMap(c -> !c, d -> -d)); + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..6792fa4d2 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/choice/Choice5Test.java @@ -0,0 +1,56 @@ +package com.jnape.palatable.lambda.adt.choice; + +import org.junit.Before; +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.choice.Choice5.a; +import static com.jnape.palatable.lambda.adt.choice.Choice5.b; +import static com.jnape.palatable.lambda.adt.choice.Choice5.c; +import static com.jnape.palatable.lambda.adt.choice.Choice5.d; +import static com.jnape.palatable.lambda.adt.choice.Choice5.e; +import static org.junit.Assert.assertEquals; + +public class Choice5Test { + + private Choice5 a; + private Choice5 b; + private Choice5 c; + private Choice5 d; + private Choice5 e; + + @Before + public void setUp() { + a = a(1); + b = b("two"); + c = c(true); + d = d(4d); + e = e('z'); + } + + @Test + public void convergeStaysInChoice() { + assertEquals(Choice4.a(1), a.converge(e -> Choice4.b(e.toString()))); + assertEquals(Choice4.b("two"), b.converge(e -> Choice4.b(e.toString()))); + assertEquals(Choice4.c(true), c.converge(e -> Choice4.b(e.toString()))); + 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 functorProperties() { + assertEquals(a, a.fmap(Character::toUpperCase)); + assertEquals(b, b.fmap(Character::toUpperCase)); + assertEquals(c, c.fmap(Character::toUpperCase)); + assertEquals(d, d.fmap(Character::toUpperCase)); + assertEquals(e('Z'), e.fmap(Character::toUpperCase)); + } + + @Test + public void bifunctorProperties() { + assertEquals(a, a.biMap(d -> -d, Character::toUpperCase)); + assertEquals(b, b.biMap(d -> -d, Character::toUpperCase)); + assertEquals(c, c.biMap(d -> -d, Character::toUpperCase)); + assertEquals(d(-4D), d.biMap(d -> -d, Character::toUpperCase)); + assertEquals(e('Z'), e.biMap(d -> -d, Character::toUpperCase)); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2Test.java b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2Test.java index af5b2ef53..3f5be9f76 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/coproduct/CoProduct2Test.java @@ -1,18 +1,15 @@ package com.jnape.palatable.lambda.adt.coproduct; -import com.jnape.palatable.traitor.annotations.TestTraits; -import com.jnape.palatable.traitor.runners.Traits; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import testsupport.traits.CoProductProjections; -import static com.jnape.palatable.lambda.adt.coproduct.CoProduct2.a; -import static com.jnape.palatable.lambda.adt.coproduct.CoProduct2.b; +import java.util.Optional; +import java.util.function.Function; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; import static org.junit.Assert.assertEquals; -@RunWith(Traits.class) public class CoProduct2Test { private CoProduct2 a; @@ -20,36 +17,35 @@ public class CoProduct2Test { @Before public void setUp() { - a = a(1); - b = b(true); - } - - @Test - public void match() { - assertEquals(1, a.match(id(), id())); - assertEquals(true, b.match(id(), id())); + a = new CoProduct2() { + @Override + public R match(Function aFn, Function bFn) { + return aFn.apply(1); + } + }; + b = new CoProduct2() { + @Override + public R match(Function aFn, Function bFn) { + return bFn.apply(true); + } + }; } @Test public void diverge() { - assertEquals(CoProduct3.a(1), a.diverge()); - assertEquals(CoProduct3.b(true), b.diverge()); - } + CoProduct3 divergeA = a.diverge(); + assertEquals(1, divergeA.match(id(), id(), id())); - @TestTraits({CoProductProjections.class}) - public Class projections() { - return CoProduct2.class; + CoProduct3 divergeB = b.diverge(); + assertEquals(true, divergeB.match(id(), id(), id())); } @Test - public void functorProperties() { - assertEquals(a, a.fmap(bool -> !bool)); - assertEquals(b(false), b.fmap(bool -> !bool)); - } + public void projections() { + assertEquals(tuple(Optional.of(1), Optional.empty()), a.project()); + assertEquals(tuple(Optional.empty(), Optional.of(true)), b.project()); - @Test - public void bifunctorProperties() { - assertEquals(a(-1), a.biMap(i -> i * -1, bool -> !bool)); - assertEquals(b(false), b.biMap(i -> i * -1, bool -> !bool)); + assertEquals(tuple(a.projectA(), a.projectB()), a.project()); + assertEquals(tuple(b.projectA(), b.projectB()), b.project()); } } \ 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 48cdfc1bf..2288b3dd2 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,21 +1,15 @@ package com.jnape.palatable.lambda.adt.coproduct; -import com.jnape.palatable.traitor.annotations.TestTraits; -import com.jnape.palatable.traitor.runners.Traits; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import testsupport.traits.CoProductProjections; +import java.util.Optional; import java.util.function.Function; -import static com.jnape.palatable.lambda.adt.coproduct.CoProduct3.a; -import static com.jnape.palatable.lambda.adt.coproduct.CoProduct3.b; -import static com.jnape.palatable.lambda.adt.coproduct.CoProduct3.c; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; import static org.junit.Assert.assertEquals; -@RunWith(Traits.class) public class CoProduct3Test { private CoProduct3 a; @@ -24,9 +18,27 @@ public class CoProduct3Test { @Before public void setUp() { - a = a(1); - b = b("two"); - c = c(true); + a = new CoProduct3() { + @Override + public R match(Function aFn, Function bFn, + Function cFn) { + return aFn.apply(1); + } + }; + b = new CoProduct3() { + @Override + public R match(Function aFn, Function bFn, + Function cFn) { + return bFn.apply("two"); + } + }; + c = new CoProduct3() { + @Override + public R match(Function aFn, Function bFn, + Function cFn) { + return cFn.apply(true); + } + }; } @Test @@ -38,36 +50,44 @@ public void match() { @Test public void diverge() { - assertEquals(CoProduct4.a(1), a.diverge()); - assertEquals(CoProduct4.b("two"), b.diverge()); - assertEquals(CoProduct4.c(true), c.diverge()); + assertEquals(1, a.diverge().match(id(), id(), id(), id())); + assertEquals("two", b.diverge().match(id(), id(), id(), id())); + assertEquals(true, c.diverge().match(id(), id(), id(), id())); } @Test public void converge() { - Function> convergenceFn = x -> x ? CoProduct2.a(1) : CoProduct2.b("false"); - assertEquals(CoProduct2.a(1), a.converge(convergenceFn)); - assertEquals(CoProduct2.b("two"), b.converge(convergenceFn)); - assertEquals(CoProduct2.a(1), CoProduct3.c(true).converge(convergenceFn)); - assertEquals(CoProduct2.b("false"), CoProduct3.c(false).converge(convergenceFn)); - } - - @TestTraits({CoProductProjections.class}) - public Class projections() { - return CoProduct3.class; + 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"); + } + }; + 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())); } @Test - public void functorProperties() { - assertEquals(a, a.fmap(bool -> !bool)); - assertEquals(b, b.fmap(bool -> !bool)); - assertEquals(c(false), c.fmap(bool -> !bool)); - } + public void projections() { + assertEquals(tuple(Optional.of(1), Optional.empty(), Optional.empty()), a.project()); + assertEquals(tuple(Optional.empty(), Optional.of("two"), Optional.empty()), b.project()); + assertEquals(tuple(Optional.empty(), Optional.empty(), Optional.of(true)), c.project()); - @Test - public void bifunctorProperties() { - assertEquals(a, a.biMap(String::toUpperCase, bool -> !bool)); - assertEquals(b("TWO"), b.biMap(String::toUpperCase, bool -> !bool)); - assertEquals(c(false), c.biMap(String::toUpperCase, bool -> !bool)); + assertEquals(tuple(a.projectA(), a.projectB(), a.projectC()), a.project()); + assertEquals(tuple(b.projectA(), b.projectB(), b.projectC()), b.project()); + assertEquals(tuple(c.projectA(), c.projectB(), c.projectC()), c.project()); } } \ No newline at end of file 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 931ac3e94..015aed644 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,22 +1,19 @@ package com.jnape.palatable.lambda.adt.coproduct; -import com.jnape.palatable.traitor.annotations.TestTraits; -import com.jnape.palatable.traitor.runners.Traits; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import testsupport.traits.CoProductProjections; +import java.util.Optional; import java.util.function.Function; -import static com.jnape.palatable.lambda.adt.coproduct.CoProduct4.a; -import static com.jnape.palatable.lambda.adt.coproduct.CoProduct4.b; -import static com.jnape.palatable.lambda.adt.coproduct.CoProduct4.c; -import static com.jnape.palatable.lambda.adt.coproduct.CoProduct4.d; +import static com.jnape.palatable.lambda.adt.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; -@RunWith(Traits.class) public class CoProduct4Test { private CoProduct4 a; @@ -42,45 +39,70 @@ public void match() { @Test public void diverge() { - assertEquals(CoProduct5.a(1), a.diverge()); - assertEquals(CoProduct5.b("two"), b.diverge()); - assertEquals(CoProduct5.c(true), c.diverge()); - assertEquals(CoProduct5.d(4D), d.diverge()); + assertEquals(1, a.diverge().match(id(), id(), id(), id(), id())); + assertEquals("two", b.diverge().match(id(), id(), id(), id(), id())); + assertEquals(true, c.diverge().match(id(), id(), id(), id(), id())); + assertEquals(4D, d.diverge().match(id(), id(), id(), id(), id())); } @Test public void converge() { - Function> convergenceFn = x -> x.equals(1d) - ? CoProduct3.a(1) - : x.equals(2d) - ? CoProduct3.b("b") - : CoProduct3.c(false); - assertEquals(CoProduct3.a(1), a.converge(convergenceFn)); - assertEquals(CoProduct3.b("two"), b.converge(convergenceFn)); - assertEquals(CoProduct3.c(true), c.converge(convergenceFn)); - assertEquals(CoProduct3.a(1), CoProduct4.d(1d).converge(convergenceFn)); - assertEquals(CoProduct3.b("b"), CoProduct4.d(2d).converge(convergenceFn)); - assertEquals(CoProduct3.c(false), CoProduct4.d(3d).converge(convergenceFn)); - } - - @TestTraits({CoProductProjections.class}) - public Class projections() { - return CoProduct4.class; + 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); + } + }; + 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())); } @Test - public void functorProperties() { - assertEquals(a, a.fmap(d -> -d)); - assertEquals(b, b.fmap(d -> -d)); - assertEquals(c, c.fmap(d -> -d)); - assertEquals(d(-4D), d.fmap(d -> -d)); - } + public void projections() { + assertEquals(tuple(Optional.of(1), Optional.empty(), Optional.empty(), Optional.empty()), a.project()); + assertEquals(tuple(Optional.empty(), Optional.of("two"), Optional.empty(), Optional.empty()), b.project()); + assertEquals(tuple(Optional.empty(), Optional.empty(), Optional.of(true), Optional.empty()), c.project()); + assertEquals(tuple(Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(4D)), d.project()); - @Test - public void bifunctorProperties() { - assertEquals(a, a.biMap(c -> !c, d -> -d)); - assertEquals(b, b.biMap(c -> !c, d -> -d)); - assertEquals(c(false), c.biMap(c -> !c, d -> -d)); - assertEquals(d(-4D), d.biMap(c -> !c, d -> -d)); + assertEquals(tuple(a.projectA(), a.projectB(), a.projectC(), a.projectD()), a.project()); + assertEquals(tuple(b.projectA(), b.projectB(), b.projectC(), b.projectD()), b.project()); + assertEquals(tuple(c.projectA(), c.projectB(), c.projectC(), c.projectD()), c.project()); + assertEquals(tuple(d.projectA(), d.projectB(), d.projectC(), d.projectD()), d.project()); } } \ No newline at end of file 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 0e2406d3a..5a34cc763 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,23 +1,15 @@ package com.jnape.palatable.lambda.adt.coproduct; -import com.jnape.palatable.traitor.annotations.TestTraits; -import com.jnape.palatable.traitor.runners.Traits; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; -import testsupport.traits.CoProductProjections; +import java.util.Optional; import java.util.function.Function; -import static com.jnape.palatable.lambda.adt.coproduct.CoProduct5.a; -import static com.jnape.palatable.lambda.adt.coproduct.CoProduct5.b; -import static com.jnape.palatable.lambda.adt.coproduct.CoProduct5.c; -import static com.jnape.palatable.lambda.adt.coproduct.CoProduct5.d; -import static com.jnape.palatable.lambda.adt.coproduct.CoProduct5.e; +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; import static com.jnape.palatable.lambda.functions.builtin.fn1.Id.id; import static org.junit.Assert.assertEquals; -@RunWith(Traits.class) public class CoProduct5Test { private CoProduct5 a; @@ -28,11 +20,46 @@ public class CoProduct5Test { @Before public void setUp() { - a = a(1); - b = b("two"); - c = c(true); - d = d(4D); - e = e('z'); + a = new CoProduct5() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn) { + return aFn.apply(1); + } + }; + b = new CoProduct5() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn) { + return bFn.apply("two"); + } + }; + c = new CoProduct5() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn) { + return cFn.apply(true); + } + }; + d = new CoProduct5() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn) { + return dFn.apply(4d); + } + }; + e = new CoProduct5() { + @Override + public R match(Function aFn, Function bFn, + Function cFn, Function dFn, + Function eFn) { + return eFn.apply('z'); + } + }; } @Test @@ -46,43 +73,92 @@ public void match() { @Test public void converge() { - Function> convergenceFn = x -> x.equals('a') - ? CoProduct4.a(1) - : x.equals('b') - ? CoProduct4.b("b") - : x.equals('c') - ? CoProduct4.c(false) - : CoProduct4.d(1d); - assertEquals(CoProduct4.a(1), a.converge(convergenceFn)); - assertEquals(CoProduct4.b("two"), b.converge(convergenceFn)); - assertEquals(CoProduct4.c(true), c.converge(convergenceFn)); - assertEquals(CoProduct4.d(4D), d.converge(convergenceFn)); - assertEquals(CoProduct4.a(1), CoProduct5.e('a').converge(convergenceFn)); - assertEquals(CoProduct4.b("b"), CoProduct5.e('b').converge(convergenceFn)); - assertEquals(CoProduct4.c(false), CoProduct5.e('c').converge(convergenceFn)); - assertEquals(CoProduct4.d(1d), CoProduct5.e('d').converge(convergenceFn)); - } - - @TestTraits({CoProductProjections.class}) - public Class projections() { - return CoProduct5.class; + 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); + } + }; + 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())); } @Test - public void functorProperties() { - assertEquals(a, a.fmap(Character::toUpperCase)); - assertEquals(b, b.fmap(Character::toUpperCase)); - assertEquals(c, c.fmap(Character::toUpperCase)); - assertEquals(d, d.fmap(Character::toUpperCase)); - assertEquals(e('Z'), e.fmap(Character::toUpperCase)); - } + public void projections() { + assertEquals(tuple(Optional.of(1), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()), a.project()); + assertEquals(tuple(Optional.empty(), Optional.of("two"), Optional.empty(), Optional.empty(), Optional.empty()), b.project()); + assertEquals(tuple(Optional.empty(), Optional.empty(), Optional.of(true), Optional.empty(), Optional.empty()), c.project()); + assertEquals(tuple(Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(4D), Optional.empty()), d.project()); + assertEquals(tuple(Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.of('z')), e.project()); - @Test - public void bifunctorProperties() { - assertEquals(a, a.biMap(d -> -d, Character::toUpperCase)); - assertEquals(b, b.biMap(d -> -d, Character::toUpperCase)); - assertEquals(c, c.biMap(d -> -d, Character::toUpperCase)); - assertEquals(d(-4D), d.biMap(d -> -d, Character::toUpperCase)); - assertEquals(e('Z'), e.biMap(d -> -d, Character::toUpperCase)); + assertEquals(tuple(a.projectA(), a.projectB(), a.projectC(), a.projectD(), a.projectE()), a.project()); + assertEquals(tuple(b.projectA(), b.projectB(), b.projectC(), b.projectD(), b.projectE()), b.project()); + assertEquals(tuple(c.projectA(), c.projectB(), c.projectC(), c.projectD(), c.projectE()), c.project()); + assertEquals(tuple(d.projectA(), d.projectB(), d.projectC(), d.projectD(), d.projectE()), d.project()); + assertEquals(tuple(e.projectA(), e.projectB(), e.projectC(), e.projectD(), e.projectE()), e.project()); } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java index 0719bd5f2..b1ff6a325 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple2Test.java @@ -55,6 +55,17 @@ public void randomAccess() { verifyNoMoreInteractions(spiedTail); } + @Test + public void into() { + Tuple2 tuple = tuple("foo", 1); + assertEquals("foo1", tuple.into((s, i) -> s + i)); + } + + @Test + public void fill() { + assertEquals(tuple("foo", "foo"), Tuple2.fill("foo")); + } + @Test public void functorProperties() { assertEquals(new Tuple2<>(1, new SingletonHList<>("2")), tuple2.fmap(Object::toString)); diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java index a354a350c..5f5dad667 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple3Test.java @@ -54,6 +54,17 @@ public void randomAccess() { verifyNoMoreInteractions(spiedTail); } + @Test + public void into() { + Tuple3 tuple = tuple("foo", 1, 2.0d); + assertEquals("foo12.0", tuple.into((s, i, d) -> s + i + d)); + } + + @Test + public void fill() { + assertEquals(tuple("foo", "foo", "foo"), Tuple3.fill("foo")); + } + @Test public void functorProperties() { assertEquals(new Tuple3<>(1, new Tuple2<>("2", new SingletonHList<>("3"))), tuple3.fmap(Object::toString)); diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java index 75567e324..ebaca3c5a 100644 --- a/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/hlist/Tuple4Test.java @@ -57,6 +57,17 @@ public void randomAccess() { verifyNoMoreInteractions(spiedTail); } + @Test + public void into() { + Tuple4 tuple = tuple("foo", 1, 2.0d, false); + assertEquals("foo12.0false", tuple.into((s, i, d, b) -> s + i + d + b)); + } + + @Test + public void fill() { + assertEquals(tuple("foo", "foo", "foo", "foo"), Tuple4.fill("foo")); + } + @Test public void functorProperties() { assertEquals(new Tuple4<>(1, new Tuple3<>("2", new Tuple2<>('3', new SingletonHList<>(true)))), tuple4.fmap(x -> !x)); 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 febdb0007..b6065098c 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 @@ -61,6 +61,11 @@ public void randomAccess() { verifyNoMoreInteractions(spiedTail); } + @Test + public void fill() { + assertEquals(tuple("foo", "foo", "foo", "foo", "foo"), Tuple5.fill("foo")); + } + @Test public void functorProperties() { assertEquals(new Tuple5<>(1, new Tuple4<>("2", new Tuple3<>('3', new Tuple2<>(false, new SingletonHList<>("5"))))), diff --git a/src/test/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKeyTest.java b/src/test/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKeyTest.java new file mode 100644 index 000000000..dbd29379e --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/hmap/TypeSafeKeyTest.java @@ -0,0 +1,14 @@ +package com.jnape.palatable.lambda.adt.hmap; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.hmap.TypeSafeKey.typeSafeKey; +import static org.junit.Assert.assertFalse; + +public class TypeSafeKeyTest { + + @Test + public void usesReferenceEquality() { + assertFalse(typeSafeKey().equals(typeSafeKey())); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/LastTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/LastTest.java new file mode 100644 index 000000000..142ba988f --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/LastTest.java @@ -0,0 +1,23 @@ +package com.jnape.palatable.lambda.functions.builtin.fn1; + +import org.junit.Test; + +import java.util.Optional; + +import static com.jnape.palatable.lambda.functions.builtin.fn1.Last.last; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static org.junit.Assert.assertEquals; + +public class LastTest { + + @Test + public void presentForNonEmptyIterable() { + assertEquals(Optional.of(3), last(asList(1, 2, 3))); + } + + @Test + public void emptyForEmpyIterables() { + assertEquals(Optional.empty(), last(emptyList())); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/ConsTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/ConsTest.java new file mode 100644 index 000000000..8922b0a86 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/ConsTest.java @@ -0,0 +1,36 @@ +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.Laziness; + +import static com.jnape.palatable.lambda.functions.builtin.fn2.Cons.cons; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static org.junit.Assert.assertThat; +import static testsupport.matchers.IterableMatcher.iterates; + +@RunWith(Traits.class) +public class ConsTest { + + @TestTraits({Laziness.class, ImmutableIteration.class, FiniteIteration.class, EmptyIterableSupport.class}) + public Fn1, Iterable> createTestSubject() { + return cons(0); + } + + @Test + public void consingElementToHeadOfIterable() { + assertThat(cons(0, asList(1, 2, 3)), iterates(0, 1, 2, 3)); + } + + @Test + public void consingToEmptyIterable() { + assertThat(cons("foo", emptyList()), iterates("foo")); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/IntersperseTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/IntersperseTest.java new file mode 100644 index 000000000..c87acc89e --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/IntersperseTest.java @@ -0,0 +1,37 @@ +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.FiniteIteration; +import testsupport.traits.ImmutableIteration; +import testsupport.traits.InfiniteIterableSupport; +import testsupport.traits.Laziness; + +import static com.jnape.palatable.lambda.functions.builtin.fn2.Intersperse.intersperse; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static org.junit.Assert.assertThat; +import static testsupport.matchers.IterableMatcher.isEmpty; +import static testsupport.matchers.IterableMatcher.iterates; + +@RunWith(Traits.class) +public class IntersperseTest { + + @TestTraits({Laziness.class, FiniteIteration.class, ImmutableIteration.class, InfiniteIterableSupport.class}) + public Fn1, Iterable> testSubject() { + return intersperse(0); + } + + @Test + public void interspersesBetweenElementsInIterable() { + assertThat(intersperse(0, asList(1, 2, 3)), iterates(1, 0, 2, 0, 3)); + } + + @Test + public void doesNotIntersperseEmptyIterable() { + assertThat(intersperse(0, emptyList()), isEmpty()); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/PrependAllTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/PrependAllTest.java new file mode 100644 index 000000000..ab0040341 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/PrependAllTest.java @@ -0,0 +1,39 @@ +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.PrependAll.prependAll; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static org.junit.Assert.assertThat; +import static testsupport.matchers.IterableMatcher.isEmpty; +import static testsupport.matchers.IterableMatcher.iterates; + +@RunWith(Traits.class) +public class PrependAllTest { + + @TestTraits({Laziness.class, FiniteIteration.class, EmptyIterableSupport.class, ImmutableIteration.class, InfiniteIterableSupport.class}) + public Fn1, Iterable> testSubject() { + return prependAll(0); + } + + @Test + public void prependsValueToAllElementsInIterable() { + Iterable ints = asList(1, 2, 3); + assertThat(prependAll(0, ints), iterates(0, 1, 0, 2, 0, 3)); + } + + @Test + public void prependingToEmptyIterableIsStillEmpty() { + assertThat(prependAll(0, emptyList()), isEmpty()); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/ToCollectionTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToCollectionTest.java similarity index 80% rename from src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/ToCollectionTest.java rename to src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToCollectionTest.java index 8593e4863..584dd876d 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn1/ToCollectionTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToCollectionTest.java @@ -1,4 +1,4 @@ -package com.jnape.palatable.lambda.functions.builtin.fn1; +package com.jnape.palatable.lambda.functions.builtin.fn2; import org.junit.Test; @@ -6,7 +6,7 @@ import java.util.Collection; import java.util.function.Supplier; -import static com.jnape.palatable.lambda.functions.builtin.fn1.ToCollection.toCollection; +import static com.jnape.palatable.lambda.functions.builtin.fn2.ToCollection.toCollection; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToMapTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToMapTest.java new file mode 100644 index 000000000..21ab43c8d --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/fn2/ToMapTest.java @@ -0,0 +1,25 @@ +package com.jnape.palatable.lambda.functions.builtin.fn2; + +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static com.jnape.palatable.lambda.adt.hlist.HList.tuple; +import static com.jnape.palatable.lambda.functions.builtin.fn2.ToMap.toMap; +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; + +public class ToMapTest { + + @Test + public void collectsEntriesIntoMap() { + Map expected = new HashMap() {{ + put("foo", 1); + put("bar", 2); + put("baz", 3); + }}; + + assertEquals(expected, toMap().apply(HashMap::new, asList(tuple("foo", 1), tuple("bar", 2), tuple("baz", 3)))); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/iterators/ConsingIteratorTest.java b/src/test/java/com/jnape/palatable/lambda/iterators/ConsingIteratorTest.java new file mode 100644 index 000000000..467a4b138 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/iterators/ConsingIteratorTest.java @@ -0,0 +1,61 @@ +package com.jnape.palatable.lambda.iterators; + +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; + +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.Take.take; +import static com.jnape.palatable.lambda.functions.builtin.fn3.FoldRight.foldRight; +import static java.util.Arrays.asList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class ConsingIteratorTest { + + private ConsingIterator consingIterator; + + @Before + public void setUp() { + consingIterator = new ConsingIterator<>(0, asList(1, 2, 3)); + } + + @Test + public void hasNextIfHeadNotYetIterated() { + assertTrue(consingIterator.hasNext()); + } + + @Test + public void nextIsHeadIfNotYetIterated() { + assertEquals((Integer) 0, consingIterator.next()); + } + + @Test + public void hasNextIfMoreElementsAfterHead() { + consingIterator.next(); + assertTrue(consingIterator.hasNext()); + } + + @Test + public void doesNotHaveNextIfNoElementsLeft() { + consingIterator.next(); + consingIterator.next(); + consingIterator.next(); + consingIterator.next(); + assertFalse(consingIterator.hasNext()); + } + + @Test + public void stackSafety() { + Integer stackBlowingNumber = 1000000; + Iterable ints = foldRight((x, acc) -> () -> new ConsingIterator<>(x, acc), + (Iterable) Collections.emptyList(), + take(stackBlowingNumber, iterate(x -> x + 1, 1))); + + assertEquals(stackBlowingNumber, + take(1, drop(stackBlowingNumber - 1, ints)).iterator().next()); + } +} \ 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 eba7e2869..9d2047c3d 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 @@ -14,8 +14,11 @@ 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 java.util.Arrays.asList; import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonMap; +import static java.util.Collections.unmodifiableMap; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; @@ -134,4 +137,27 @@ public void invertedFocusesOnMapWithKeysAndValuesSwitched() { put(1, "foo"); }}, view(inverted, withDuplicateValues)); } + + @Test + public void mappingValuesRetainsMapStructureWithMappedValues() { + Map m = unmodifiableMap(new HashMap() {{ + put("foo", "1"); + put("bar", "2"); + put("baz", "3"); + }}); + Lens.Simple, Map> mappingValues = mappingValues(Integer::parseInt); + + assertEquals(new HashMap() {{ + put("foo", 1); + put("bar", 2); + put("baz", 3); + }}, view(mappingValues, m)); + + Map updated = set(mappingValues, unmodifiableMap(new HashMap() {{ + put("foo", 2); + put("bar", 1); + put("baz", 3); + }}), m); + assertEquals(singletonMap("baz", "3"), updated); + } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/AndTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/AndTest.java new file mode 100644 index 000000000..8ae51c840 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/AndTest.java @@ -0,0 +1,23 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.monoid.builtin.And.and; +import static org.junit.Assert.assertEquals; + +public class AndTest { + + @Test + public void identity() { + assertEquals(true, and().identity()); + } + + @Test + public void monoid() { + And and = and(); + assertEquals(true, and.apply(true, true)); + assertEquals(false, and.apply(false, true)); + assertEquals(false, and.apply(true, false)); + assertEquals(false, and.apply(false, false)); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/FirstTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/FirstTest.java new file mode 100644 index 000000000..149dd686d --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/FirstTest.java @@ -0,0 +1,25 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import org.junit.Test; + +import java.util.Optional; + +import static com.jnape.palatable.lambda.monoid.builtin.First.first; +import static org.junit.Assert.assertEquals; + +public class FirstTest { + + @Test + public void identity() { + assertEquals(Optional.empty(), first().identity()); + } + + @Test + public void monoid() { + First first = first(); + assertEquals(Optional.of(1), first.apply(Optional.of(1), Optional.of(2))); + assertEquals(Optional.of(1), first.apply(Optional.of(1), Optional.empty())); + assertEquals(Optional.of(2), first.apply(Optional.empty(), Optional.of(2))); + assertEquals(Optional.empty(), first.apply(Optional.empty(), Optional.empty())); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/JoinTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/JoinTest.java new file mode 100644 index 000000000..fb78013f6 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/JoinTest.java @@ -0,0 +1,22 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.monoid.builtin.Join.join; +import static org.junit.Assert.assertEquals; + +public class JoinTest { + + @Test + public void identity() { + assertEquals("", join().identity()); + } + + @Test + public void monoid() { + assertEquals("ab", join().apply("a", "b")); + assertEquals("a", join().apply("a", "")); + assertEquals("b", join().apply("", "b")); + assertEquals("", join().apply("", "")); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/LastTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/LastTest.java new file mode 100644 index 000000000..339c6204c --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/LastTest.java @@ -0,0 +1,25 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import org.junit.Test; + +import java.util.Optional; + +import static com.jnape.palatable.lambda.monoid.builtin.Last.last; +import static org.junit.Assert.assertEquals; + +public class LastTest { + + @Test + public void identity() { + assertEquals(Optional.empty(), last().identity()); + } + + @Test + public void monoid() { + Last last = last(); + assertEquals(Optional.of(2), last.apply(Optional.of(1), Optional.of(2))); + assertEquals(Optional.of(2), last.apply(Optional.empty(), Optional.of(2))); + assertEquals(Optional.of(1), last.apply(Optional.of(1), Optional.empty())); + assertEquals(Optional.empty(), last.apply(Optional.empty(), Optional.empty())); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/monoid/builtin/OrTest.java b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/OrTest.java new file mode 100644 index 000000000..336a98e4d --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/monoid/builtin/OrTest.java @@ -0,0 +1,23 @@ +package com.jnape.palatable.lambda.monoid.builtin; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.monoid.builtin.Or.or; +import static org.junit.Assert.assertEquals; + +public class OrTest { + + @Test + public void identity() { + assertEquals(false, or().identity()); + } + + @Test + public void monoid() { + Or or = or(); + assertEquals(true, or.apply(true, true)); + assertEquals(true, or.apply(true, false)); + assertEquals(true, or.apply(false, true)); + assertEquals(false, or.apply(false, false)); + } +} \ No newline at end of file diff --git a/src/test/java/testsupport/traits/CoProductProjections.java b/src/test/java/testsupport/traits/CoProductProjections.java deleted file mode 100644 index 77aa47d98..000000000 --- a/src/test/java/testsupport/traits/CoProductProjections.java +++ /dev/null @@ -1,86 +0,0 @@ -package testsupport.traits; - -import com.jnape.palatable.traitor.traits.Trait; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import static com.jnape.palatable.lambda.functions.builtin.fn1.Size.size; -import static com.jnape.palatable.lambda.functions.builtin.fn1.ToCollection.toCollection; -import static com.jnape.palatable.lambda.functions.builtin.fn2.Filter.filter; -import static com.jnape.palatable.lambda.functions.builtin.fn2.Map.map; -import static java.lang.String.format; -import static java.util.Arrays.asList; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -public final class CoProductProjections implements Trait> { - - @Override - public void test(Class coProductType) { - List declaredMethods = asList(coProductType.getDeclaredMethods()); - Iterable staticFactoryMethods = filter(m -> m.getName().length() == 1, declaredMethods); - List staticFactoryMethodNames = toCollection(ArrayList::new, map(Method::getName, staticFactoryMethods)); - Collections.sort(staticFactoryMethodNames); - - if (size(staticFactoryMethodNames) == 0) - fail("Couldn't find any static constructors for type " + coProductType.getSimpleName()); - - assertIndividualProjections(coProductType, staticFactoryMethods); - assertFullProjection(coProductType, staticFactoryMethods, staticFactoryMethodNames); - } - - private void assertFullProjection(Class coProductType, Iterable staticFactoryMethods, - List staticFactoryMethodNames) { - staticFactoryMethods.forEach(sfm -> { - try { - String value = "value"; - Object instance = sfm.invoke(null, value); - Method project = coProductType.getDeclaredMethod("project"); - Object tuple = project.invoke(instance); - for (int i = 0; i < staticFactoryMethodNames.size(); i++) { - Object slot = tuple.getClass().getDeclaredMethod("_" + (i + 1)).invoke(tuple); - if (i == staticFactoryMethodNames.indexOf(sfm.getName())) - assertEquals(format("Assertion %1$s.%2$s(\"%3$s\")._%4$s() == Optional.of(\"%3$s\")", - coProductType.getSimpleName(), - sfm.getName(), - value, - i + 1), Optional.of(value), slot); - else - assertEquals(format("Assertion %1$s.%2$s(\"%3$s\")._%4$s() == Optional.empty()", - coProductType.getSimpleName(), - sfm.getName(), - value, - i + 1), Optional.empty(), slot); - } - } catch (IllegalAccessException | InvocationTargetException e) { - fail("Trait " + getClass().getSimpleName() + " is broken: " + e.getMessage()); - } catch (NoSuchMethodException e) { - fail(coProductType.getSimpleName() + " is missing an expected method: " + e.getMessage()); - } - }); - } - - private void assertIndividualProjections(Class coProductType, Iterable staticFactoryMethods) { - staticFactoryMethods.forEach(sfm -> { - try { - String value = "value"; - Object instance = sfm.invoke(null, value); - Method project = coProductType.getDeclaredMethod("project" + sfm.getName().toUpperCase()); - assertEquals(format("Assertion %1$s.%2$s(\"%3$s\").%4$s() == Optional.of(\"%3$s\")", - coProductType.getSimpleName(), - sfm.getName(), - value, - project.getName()), Optional.of(value), project.invoke(instance)); - } catch (IllegalAccessException | InvocationTargetException e) { - fail("Trait " + getClass().getSimpleName() + " is broken: " + e.getMessage()); - } catch (NoSuchMethodException e) { - fail(coProductType.getSimpleName() + " is missing an expected method: " + e.getMessage()); - } - }); - } -} diff --git a/src/test/java/testsupport/traits/InfiniteIterableSupport.java b/src/test/java/testsupport/traits/InfiniteIterableSupport.java new file mode 100644 index 000000000..3c870f1ad --- /dev/null +++ b/src/test/java/testsupport/traits/InfiniteIterableSupport.java @@ -0,0 +1,28 @@ +package testsupport.traits; + +import com.jnape.palatable.lambda.functions.Fn1; +import com.jnape.palatable.traitor.traits.Trait; + +import java.util.concurrent.CountDownLatch; + +import static com.jnape.palatable.lambda.functions.builtin.fn1.Repeat.repeat; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.fail; + +public class InfiniteIterableSupport implements Trait> { + + @Override + public void test(Fn1 testSubject) { + CountDownLatch latch = new CountDownLatch(1); + new Thread(() -> { + testSubject.apply(repeat(0)).iterator().next(); + latch.countDown(); + }).start(); + try { + if (!latch.await(1, SECONDS)) + fail("Termination when given an infinite iterable could not be proven in time"); + } catch (InterruptedException e) { + throw new AssertionError("interrupted", e); + } + } +}