diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..9bcf99945 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,3 @@ +language: java +jdk: + - oraclejdk8 diff --git a/README.md b/README.md index dfc2cbf17..9d819fb8f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,188 @@ lambda ====== +[![Build Status](https://travis-ci.org/palatable/lambda.svg)](https://travis-ci.org/palatable/lambda) -Functional patterns for Java 8 \ No newline at end of file +Functional patterns for Java 8 + +Installation +------------ + +Add the following dependency to your: + +`pom.xml` ([Maven](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html)): + +```xml + + com.jnape.palatable + lambda + 1.0 + +``` + +`build.gradle` ([Gradle](https://docs.gradle.org/current/userguide/dependency_management.html)): + +```gradle + compile group: 'com.jnape.palatable', name: 'lambda', version: '1.0' +``` + +Background +---------- + +Lambda was born out of a desire to use some of the same canonical functions (e.g. `unfoldr`, `takeWhile`, `zipWith`) and functional patterns (e.g. `Functor` and friends) that are idiomatic in other languages and make them available for Java. + +Some things a user of lambda most likely values: + +- Lazy evaluation +- Immutablility by design +- Composition +- Higher-level abstractions +- Parametric polymorphism + +Generally, everything that lambda produces is lazily-evaluated (except for terminal operations like `reduce`), immutable (except for `Iterator`s, since it's effectively impossible), composable (even between different arities, where possible), foundational (maximally contravariant), and parametrically type-checked (even where this adds unnecessary constraints due to a lack of higher-kinded types). + +Although the library is currently (very) small, these values should always be the driving forces behind future growth. + +Examples +-------- + +First, the obligatory `map`/`filter`/`reduce` example: +```Java + Integer sumOfEvenIncrements = + reduceLeft((x, y) -> x + y, + filter(x -> x % 2 == 0, + map(x -> x + 1, asList(1, 2, 3, 4, 5)))); + //-> 12 +``` + +Every function in lambda is [curried](https://www.wikiwand.com/en/Currying), so we could have also done this: +```Java + MonadicFunction, Integer> sumOfEvenIncrementsFn = + map((Integer x) -> x + 1) + .then(filter(x -> x % 2 == 0)) + .then(reduceLeft((x, y) -> x + y)); + + Integer sumOfEvenIncrements = sumOfEvenIncrementsFn.apply(asList(1, 2, 3, 4, 5)); + //-> 12 +``` + +How about the positive squares below 100: + +```Java + Iterable positiveSquaresBelow100 = + takeWhile(x -> x < 100, map(x -> x * x, iterate(x -> x + 1, 1))); + //-> [1, 4, 9, 16, 25, 36, 49, 64, 81] +``` + +We could have also used `unfoldr`: + +```Java + Iterable positiveSquaresBelow100 = unfoldr(x -> { + int square = x * x; + return square < 100 ? Optional.of(tuple(square, x + 1)) : Optional.empty(); + }, 1); + //-> [1, 4, 9, 16, 25, 36, 49, 64, 81] +``` + +What if we want the cross product of a domain and codomain: + +```Java + Iterable> crossProduct = + take(10, cartesianProduct(asList(1, 2, 3), asList("a", "b", "c"))); + //-> (1,"a"), (1,"b"), (1,"c"), (2,"a"), (2,"b"), (2,"c"), (3,"a"), (3,"b"), (3,"c") +``` + +Let's compose two functions: + +```Java + MonadicFunction add = x -> x + 1; + MonadicFunction subtract = x -> x -1; + + MonadicFunction noOp = add.then(subtract); + // same as + MonadicFunction alsoNoOp = subtract.fmap(add); +``` + +And partially apply some: + +```Java + DyadicFunction add = (x, y) -> x + y; + + MonadicFunction add1 = add.apply(1); + add1.apply(2); + //-> 3 +``` + +And have fun with 3s: + +```Java + Iterable> multiplesOf3InGroupsOf3 = + take(10, inGroupsOf(3, unfoldr(x -> Optional.of(tuple(x * 3, x + 1)), 1))); + //-> [[3, 6, 9], [12, 15, 18], [21, 24, 27]] +``` + +Or check out [the tests](https://github.com/palatable/lambda/tree/master/src/test/java/com/jnape/palatable/lambda/functions/builtin) for more examples. + +ADTs +---- + +In addition to the functions above, Lambda also supports a few first-class [algebraic data types](https://www.wikiwand.com/en/Algebraic_data_type). + +### Tuples + +There are two parametric variants on the tuple product type, `Tuple2<_1, _2>` and `Tuple3<_1, _2, _3>`, both immutable. + +```Java + Tuple2 tuple2 = Tuple2.tuple("foo", 1); + + System.out.println(tuple2._1); // prints "foo" + System.out.println(tuple2._2); // prints 1 +``` + +```Java + Tuple3 tuple3 = Tuple3.tuple("foo", true, 1); + + System.out.println(tuple3._1); // prints "foo" + System.out.println(tuple3._2); // prints true + System.out.println(tuple3._3); // prints 1 +``` + +Both `Tuple2` and `Tuple3` are `BiFunctor`s over `_1` and `_2`: + +```Java + Tuple2 mappedTuple2 = tuple2.biMap(String::toUpperCase, x -> x + 1); + + System.out.println(mappedTuple2._1); // prints "FOO" + System.out.println(mappedTuple2._2); // prints 2 +``` + +```Java + Tuple3 mappedTuple3 = tuple3.biMap(String::toUpperCase, b -> !b); + + System.out.println(mappedTuple3._1); // prints "FOO" + System.out.println(mappedTuple3._2); // prints false + System.out.println(mappedTuple3._3); // prints 1 +``` + +### Either + +Binary tagged unions are represented as `Either`s, which resolve to one of two possible values: a `Left` value wrapping an `L`, or a `Right` value wrapping an `R` (typically an exceptional value or a successful value, respectively). + +Rather than supporting explicit value unwrapping, `Either` supports many useful comprehensions to help facilitate type-safe interactions. For example, `Either#match` is used to resolve an `Either` to a different type. + +```Java + Either right = Either.right(1); + Either left = Either.left("Head fell off"); + + Boolean successful = right.match(l -> false, r -> true); + //-> true + + List values = left.match(l -> Collections.emptyList(), Collections::singletonList); + //-> [] +``` + +Check out the tests for [more examples](https://github.com/palatable/lambda/blob/master/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java) of ways to interact with `Either`. + +License +------- + +_lambda_ is part of [palatable](http://www.github.com/palatable), which is distributed under [The MIT License](http://choosealicense.com/licenses/mit/). diff --git a/pom.xml b/pom.xml index d268b8210..fac9872d1 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ lambda - 1.0 + 1.1 jar Lambda @@ -33,6 +33,17 @@ git@github.com:palatable/lambda.git + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + jnape @@ -43,8 +54,8 @@ 3.1 - 1.0 1.0 + 3.3 @@ -60,11 +71,7 @@ com.jnape.palatable traitor ${traitor.version} - - - com.jnape.loan-shark - loan-shark - ${loan-shark.version} + test org.apache.commons @@ -74,4 +81,18 @@ - \ No newline at end of file + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 1.8 + 1.8 + + + + + + diff --git a/src/main/java/com/jnape/palatable/lambda/adt/Either.java b/src/main/java/com/jnape/palatable/lambda/adt/Either.java new file mode 100644 index 000000000..2bb5b6c5f --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/Either.java @@ -0,0 +1,180 @@ +package com.jnape.palatable.lambda.adt; + +import com.jnape.palatable.lambda.functions.DyadicFunction; +import com.jnape.palatable.lambda.functions.MonadicFunction; +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; +import java.util.function.Supplier; + +import static com.jnape.palatable.lambda.functions.builtin.monadic.Identity.id; + +/** + * The binary tagged union. General semantics tend to connote "success" values via the right value and "failure" values + * via the left values. Eithers are both Functors over their right value and + * Bifunctors over both values. + * + * @param The left parameter type + * @param The right parameter type + */ +public abstract class Either implements Functor, Bifunctor { + + public final R or(R defaultValue) { + return recover(l -> defaultValue); + } + + public final R recover(MonadicFunction recoveryFn) { + return match(recoveryFn, id()); + } + + public final L forfeit(MonadicFunction forfeitFn) { + return match(id(), forfeitFn); + } + + public final R orThrow( + MonadicFunction throwableFn) throws E { + return match(l -> { + throw throwableFn.apply(l); + }, id()); + } + + public final Either filter(MonadicFunction pred, + Supplier leftSupplier) { + return flatMap(r -> pred.apply(r) ? right(r) : left(leftSupplier.get())); + } + + public final Either flatMap(MonadicFunction> rightFn) { + return flatMap(Either::left, rightFn); + } + + public final Either flatMap(MonadicFunction> leftFn, + MonadicFunction> rightFn) { + return match(leftFn, rightFn); + } + + public final Either merge(DyadicFunction leftFn, + DyadicFunction rightFn, + Either other) { + return this.match( + l1 -> other.match(l2 -> left(leftFn.apply(l1, l2)), r -> left(l1)), + r1 -> other.match(Either::left, r2 -> right(rightFn.apply(r1, r2)))); + } + + public abstract V match(MonadicFunction leftFn, + MonadicFunction rightFn); + + @Override + public final Either fmap(MonadicFunction fn) { + return biMapR(fn); + } + + @Override + @SuppressWarnings("unchecked") + public final Either biMapL(MonadicFunction fn) { + return (Either) Bifunctor.super.biMapL(fn); + } + + @Override + @SuppressWarnings("unchecked") + public final Either biMapR(MonadicFunction fn) { + return (Either) Bifunctor.super.biMapR(fn); + } + + @Override + public final Either biMap(MonadicFunction leftFn, + MonadicFunction rightFn) { + return match(l -> left(leftFn.apply(l)), r -> right(rightFn.apply(r))); + } + + public static Either fromOptional(Optional optional, Supplier leftFn) { + return optional.>map(Either::right) + .orElse(left(leftFn.get())); + } + + @SuppressWarnings("unchecked") + public static Either trying(CheckedSupplier supplier, + MonadicFunction leftFn) { + try { + return right(supplier.get()); + } catch (Exception e) { + return left(leftFn.apply((E) e)); + } + } + + public static Either trying(CheckedSupplier supplier) { + return trying(supplier, id()); + } + + public static Either left(L l) { + return new Left<>(l); + } + + public static Either right(R r) { + return new Right<>(r); + } + + private static final class Left extends Either { + private final L l; + + private Left(L l) { + this.l = l; + } + + @Override + public V match(MonadicFunction leftFn, + MonadicFunction rightFn) { + return leftFn.apply(l); + } + + @Override + public boolean equals(Object other) { + return other instanceof Left && Objects.equals(l, ((Left) other).l); + } + + @Override + public int hashCode() { + return Objects.hash(l); + } + + @Override + public String toString() { + return "Left{" + + "l=" + l + + '}'; + } + } + + private static final class Right extends Either { + private final R r; + + private Right(R r) { + this.r = r; + } + + @Override + public V match(MonadicFunction leftFn, + MonadicFunction rightFn) { + return rightFn.apply(r); + } + + @Override + public boolean equals(Object other) { + return other instanceof Right && Objects.equals(r, ((Right) other).r); + } + + @Override + public int hashCode() { + return Objects.hash(r); + } + + @Override + public String toString() { + return "Right{" + + "r=" + r + + '}'; + } + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/HList.java b/src/main/java/com/jnape/palatable/lambda/adt/HList.java new file mode 100644 index 000000000..13d6b4958 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/HList.java @@ -0,0 +1,168 @@ +package com.jnape.palatable.lambda.adt; + +import com.jnape.palatable.lambda.functions.MonadicFunction; +import com.jnape.palatable.lambda.functor.Functor; + +/** + * A heterogeneous list supporting arbitrary depth type-safety via a linearly recursive type signature. Note that due to + * its rapidly expanding type signature, specializations exist up to certain depths to minimize typing overhead. + * + * @param The head element type + * @param The encoded recursive tail HList type + */ +public abstract class HList> { + + private HList() { + } + + public abstract HCons> cons(NewHead newHead); + + public static HNil nil() { + return HNil.INSTANCE; + } + + public static > HCons cons(Head head, Tail tail) { + return new HCons<>(head, tail); + } + + public static HCons1 list(Head head) { + return new HCons1<>(head); + } + + public static HCons2 list(H1 h1, H2 h2) { + return list(h2).cons(h1); + } + + public static HCons3 list(H1 h1, H2 h2, H3 h3) { + return list(h2, h3).cons(h1); + } + + public static HCons4 list(H1 h1, H2 h2, H3 h3, H4 h4) { + return list(h2, h3, h4).cons(h1); + } + + public static HCons5 list(H1 h1, H2 h2, H3 h3, H4 h4, H5 h5) { + return list(h2, h3, h4, h5).cons(h1); + } + + public static final class HCons5 extends HCons> { + private HCons5(A a, HCons4 tail) { + super(a, tail); + } + + @Override + public HCons> cons(NewHead newHead) { + return new HCons<>(newHead, this); + } + } + + public static final class HCons4 extends HCons> { + private HCons4(A a, HCons3 tail) { + super(a, tail); + } + + @Override + public HCons5 cons(NewHead newHead) { + return new HCons5<>(newHead, this); + } + } + + public static final class HCons3 extends HCons> { + private HCons3(A a, HCons2 tail) { + super(a, tail); + } + + @Override + public HCons4 cons(NewHead newHead) { + return new HCons4<>(newHead, this); + } + } + + public static final class HCons2 extends HCons> { + public HCons2(A a, HCons1 tail) { + super(a, tail); + } + + @Override + public HCons3 cons(NewHead newHead) { + return new HCons3<>(newHead, this); + } + } + + public static final class HCons1 extends HCons { + private HCons1(Head head) { + super(head, nil()); + } + + @Override + public HCons2 cons(NewHead newHead) { + return new HCons2<>(newHead, this); + } + } + + public static class HCons> extends HList implements Functor { + private final Head head; + private final Tail tail; + + private HCons(Head head, Tail tail) { + this.head = head; + this.tail = tail; + } + + public Head head() { + return head; + } + + public Tail tail() { + return tail; + } + + @Override + public HCons> cons(NewHead newHead) { + return new HCons<>(newHead, this); + } + + @Override + public HCons fmap(MonadicFunction fn) { + return new HCons<>(fn.apply(head), tail); + } + + @Override + public boolean equals(Object other) { + if (other instanceof HCons) { + HCons that = (HCons) other; + boolean sameHead = this.head.equals(that.head); + boolean sameTail = this.tail.equals(that.tail); + return sameHead && sameTail; + } + return false; + } + + @Override + public int hashCode() { + return 31 * head.hashCode() + tail.hashCode(); + } + + @Override + public String toString() { + return "HCons{" + + "head=" + head + + ", tail=" + tail + + '}'; + } + } + + public static final class HNil extends HList { + private static final HNil INSTANCE = new HNil(); + + @Override + public HCons cons(NewHead newHead) { + return new HCons<>(newHead, this); + } + + @Override + public String toString() { + return "HNil{}"; + } + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple2.java b/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple2.java new file mode 100644 index 000000000..e60638a5f --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple2.java @@ -0,0 +1,73 @@ +package com.jnape.palatable.lambda.adt.tuples; + +import com.jnape.palatable.lambda.functions.MonadicFunction; +import com.jnape.palatable.lambda.functor.Bifunctor; +import com.jnape.palatable.lambda.functor.Functor; + +import java.util.Objects; + +import static java.lang.String.format; + +/** + * The binary tuple product type. Tuple2 + * + * @param <_1> The first slot element type + * @param <_2> The second slot element type + * @see Tuple3 + */ +public class Tuple2<_1, _2> implements Functor<_2>, Bifunctor<_1, _2> { + + public final _1 _1; + public final _2 _2; + + Tuple2(_1 _1, _2 _2) { + this._1 = _1; + this._2 = _2; + } + + @Override + public <_2A> Tuple2<_1, _2A> fmap(MonadicFunction fn) { + return biMapR(fn); + } + + @Override + public <_1A, _2A> Tuple2<_1A, _2A> biMap(MonadicFunction f1, + MonadicFunction f2) { + return tuple(f1.apply(_1), f2.apply(_2)); + } + + @Override + @SuppressWarnings("unchecked") + public <_1A> Tuple2<_1A, _2> biMapL(MonadicFunction fn) { + return (Tuple2<_1A, _2>) Bifunctor.super.biMapL(fn); + } + + @Override + @SuppressWarnings("unchecked") + public <_2A> Tuple2<_1, _2A> biMapR(MonadicFunction fn) { + return (Tuple2<_1, _2A>) Bifunctor.super.biMapR(fn); + } + + @Override + public String toString() { + return format("(%s, %s)", _1, _2); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Tuple2 tuple2 = (Tuple2) o; + return Objects.equals(_1, tuple2._1) && + Objects.equals(_2, tuple2._2); + } + + @Override + public int hashCode() { + return Objects.hash(_1, _2); + } + + public static <_1, _2> Tuple2<_1, _2> tuple(_1 _1, _2 _2) { + return new Tuple2<>(_1, _2); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple3.java b/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple3.java new file mode 100644 index 000000000..a8b947414 --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/adt/tuples/Tuple3.java @@ -0,0 +1,74 @@ +package com.jnape.palatable.lambda.adt.tuples; + +import com.jnape.palatable.lambda.functions.MonadicFunction; + +import java.util.Objects; + +import static java.lang.String.format; + +/** + * The ternary tuple product type. This class extends {@link Tuple2} and as such embodies the same + * functorial properties. + * + * @param <_1> The first slot element type + * @param <_2> The second slot element type + * @param <_3> The third slot element type + */ +public class Tuple3<_1, _2, _3> extends Tuple2<_1, _2> { + + public final _3 _3; + + Tuple3(_1 _1, _2 _2, _3 _3) { + super(_1, _2); + this._3 = _3; + } + + @Override + @SuppressWarnings("unchecked") + public <_2A> Tuple3<_1, _2A, _3> fmap(MonadicFunction fn) { + return (Tuple3<_1, _2A, _3>) super.fmap(fn); + } + + @Override + @SuppressWarnings("unchecked") + public <_1A, _2A> Tuple3<_1A, _2A, _3> biMap(MonadicFunction f1, + MonadicFunction f2) { + return (Tuple3<_1A, _2A, _3>) super.biMap(f1, f2); + } + + @Override + @SuppressWarnings("unchecked") + public <_1A> Tuple3<_1A, _2, _3> biMapL(MonadicFunction fn) { + return (Tuple3<_1A, _2, _3>) super.biMapL(fn); + } + + @Override + @SuppressWarnings("unchecked") + public <_2A> Tuple3<_1, _2A, _3> biMapR(MonadicFunction fn) { + return (Tuple3<_1, _2A, _3>) super.biMapR(fn); + } + + @Override + public boolean equals(Object other) { + if (other instanceof Tuple3) { + Tuple3 that = (Tuple3) other; + return super.equals(other) + && Objects.equals(this._3, that._3); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), _3); + } + + @Override + public String toString() { + return format("(%s, %s, %s)", _1, _2, _3); + } + + public static <_1, _2, _3> Tuple3<_1, _2, _3> tuple(_1 _1, _2 _2, _3 _3) { + return new Tuple3<>(_1, _2, _3); + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/applicative/BiFunctor.java b/src/main/java/com/jnape/palatable/lambda/applicative/BiFunctor.java deleted file mode 100644 index 1a63dd236..000000000 --- a/src/main/java/com/jnape/palatable/lambda/applicative/BiFunctor.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.jnape.palatable.lambda.applicative; - -import com.jnape.palatable.lambda.functions.MonadicFunction; - -import static com.jnape.palatable.lambda.functions.builtin.monadic.Identity.id; - -@FunctionalInterface -public interface BiFunctor { - - default BiFunctor biMapL(MonadicFunction fn) { - return biMap(fn, id()); - } - - default BiFunctor biMapR(MonadicFunction fn) { - return biMap(id(), fn); - } - - BiFunctor biMap(MonadicFunction f1, - MonadicFunction f2); -} diff --git a/src/main/java/com/jnape/palatable/lambda/applicative/Functor.java b/src/main/java/com/jnape/palatable/lambda/applicative/Functor.java deleted file mode 100644 index 5dd64e16c..000000000 --- a/src/main/java/com/jnape/palatable/lambda/applicative/Functor.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.jnape.palatable.lambda.applicative; - -import com.jnape.palatable.lambda.functions.MonadicFunction; - -@FunctionalInterface -public interface Functor { - - Functor fmap(MonadicFunction fn); -} diff --git a/src/main/java/com/jnape/palatable/lambda/applicative/ProFunctor.java b/src/main/java/com/jnape/palatable/lambda/applicative/ProFunctor.java deleted file mode 100644 index f339501dd..000000000 --- a/src/main/java/com/jnape/palatable/lambda/applicative/ProFunctor.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.jnape.palatable.lambda.applicative; - -import com.jnape.palatable.lambda.functions.MonadicFunction; - -import static com.jnape.palatable.lambda.functions.builtin.monadic.Identity.id; - -@FunctionalInterface -public interface ProFunctor { - - default ProFunctor diMapL(MonadicFunction fn) { - return diMap(fn, id()); - } - - default ProFunctor diMapR(MonadicFunction fn) { - return diMap(id(), fn); - } - - ProFunctor diMap(MonadicFunction f1, - MonadicFunction f2); -} diff --git a/src/main/java/com/jnape/palatable/lambda/continuation/Continuation.java b/src/main/java/com/jnape/palatable/lambda/continuation/Continuation.java deleted file mode 100644 index 3c47077e0..000000000 --- a/src/main/java/com/jnape/palatable/lambda/continuation/Continuation.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.jnape.palatable.lambda.continuation; - -import com.jnape.palatable.lambda.applicative.Functor; -import com.jnape.palatable.lambda.functions.MonadicFunction; -import com.jnape.palatable.lambda.tuples.Tuple2; - -import java.util.Iterator; -import java.util.Optional; - -import static com.jnape.palatable.lambda.continuation.Memo.memoize; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; - -@FunctionalInterface -public interface Continuation extends Functor, Iterable { - - Optional>> next(); - - @Override - default Iterator iterator() { - return ContinuationIterator.wrap(this); - } - - default Continuation then(Continuation more) { - return () -> next() - .map(t -> Optional.of(tuple(t._1, t._2.then(more)))) - .orElseGet(more::next); - } - - @Override - default Continuation fmap(MonadicFunction fn) { - return (() -> next().map(t -> t.biMap(fn, c -> c.fmap(fn)))); - } - - default Continuation memoized() { - return memoize((() -> next().map(t -> tuple(t._1, t._2.memoized()))))::get; - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/continuation/ContinuationIterator.java b/src/main/java/com/jnape/palatable/lambda/continuation/ContinuationIterator.java deleted file mode 100644 index b59b54ca6..000000000 --- a/src/main/java/com/jnape/palatable/lambda/continuation/ContinuationIterator.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.jnape.palatable.lambda.continuation; - -import com.jnape.palatable.lambda.tuples.Tuple2; - -import java.util.Iterator; -import java.util.NoSuchElementException; - -public final class ContinuationIterator implements Iterator { - private Continuation continuation; - - public ContinuationIterator(Continuation continuation) { - this.continuation = continuation; - } - - @Override - public boolean hasNext() { - return continuation.next().isPresent(); - } - - @Override - public A next() { - if (!hasNext()) - throw new NoSuchElementException(); - - Tuple2> continuationResult = continuation.next().get(); - continuation = continuationResult._2; - return continuationResult._1; - } - - public static ContinuationIterator wrap(Continuation continuable) { - return new ContinuationIterator<>(continuable); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/continuation/Continuations.java b/src/main/java/com/jnape/palatable/lambda/continuation/Continuations.java deleted file mode 100644 index f788876a9..000000000 --- a/src/main/java/com/jnape/palatable/lambda/continuation/Continuations.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.jnape.palatable.lambda.continuation; - -import java.util.Iterator; -import java.util.stream.Stream; - -import static java.util.Arrays.asList; - -public final class Continuations { - - private Continuations() { - } - - @SafeVarargs - public static Continuation continuing(A... as) { - return continuing(asList(as)); - } - - public static Continuation continuing(Stream as) { - return continuing(as.iterator()); - } - - public static Continuation continuing(Iterable as) { - return continuing(as.iterator()); - } - - public static Continuation continuing(Iterator as) { - return new IteratorWrappingContinuation<>(as); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/continuation/IteratorWrappingContinuation.java b/src/main/java/com/jnape/palatable/lambda/continuation/IteratorWrappingContinuation.java deleted file mode 100644 index f6b83f2dd..000000000 --- a/src/main/java/com/jnape/palatable/lambda/continuation/IteratorWrappingContinuation.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.jnape.palatable.lambda.continuation; - -import com.jnape.palatable.lambda.tuples.Tuple2; - -import java.util.Iterator; -import java.util.Optional; - -import static com.jnape.palatable.lambda.continuation.Memo.memoize; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; - -public final class IteratorWrappingContinuation implements Continuation { - private final Memo>>> memoizedResult; - - public IteratorWrappingContinuation(Iterator iterator) { - memoizedResult = memoize(() -> (iterator.hasNext()) - ? Optional.of(tuple(iterator.next(), new IteratorWrappingContinuation<>(iterator))) - : Optional.empty()); - } - - @Override - public Optional>> next() { - return memoizedResult.get(); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/continuation/Memo.java b/src/main/java/com/jnape/palatable/lambda/continuation/Memo.java deleted file mode 100644 index 95124052c..000000000 --- a/src/main/java/com/jnape/palatable/lambda/continuation/Memo.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.jnape.palatable.lambda.continuation; - -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Supplier; - -public final class Memo { - private final AtomicReference valueRef; - private final Supplier fn; - - private Memo(Supplier fn) { - this.fn = fn; - valueRef = new AtomicReference<>(); - } - - public T get() { - if (valueRef.get() == null) - valueRef.compareAndSet(null, fn.get()); - return valueRef.get(); - } - - public static Memo memoize(Supplier fn) { - return new Memo<>(fn); - } - - public static Memo memoize(T result) { - return memoize(() -> result); - } - - -} diff --git a/src/main/java/com/jnape/palatable/lambda/exceptions/EmptyIterableException.java b/src/main/java/com/jnape/palatable/lambda/exceptions/EmptyIterableException.java deleted file mode 100644 index 267145b4a..000000000 --- a/src/main/java/com/jnape/palatable/lambda/exceptions/EmptyIterableException.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.jnape.palatable.lambda.exceptions; - -public class EmptyIterableException extends RuntimeException { - - public EmptyIterableException() { - super("Iterable was empty."); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java b/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java index 720ce2ca8..3aed1de61 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/DyadicFunction.java @@ -1,10 +1,19 @@ package com.jnape.palatable.lambda.functions; -import com.jnape.palatable.lambda.applicative.ProFunctor; -import com.jnape.palatable.lambda.tuples.Tuple2; - +import com.jnape.palatable.lambda.adt.tuples.Tuple2; +import com.jnape.palatable.lambda.functor.Profunctor; + +/** + * A function taking two arguments. Auto-curried. + * + * @param The first argument type + * @param The second argument type + * @param The return type + * @see MonadicFunction + * @see com.jnape.palatable.lambda.functions.builtin.dyadic.Partial2 + */ @FunctionalInterface -public interface DyadicFunction extends MonadicFunction>, ProFunctor { +public interface DyadicFunction extends MonadicFunction>, Profunctor { C apply(A a, B b); @@ -14,13 +23,15 @@ default MonadicFunction apply(A a) { } @Override + @SuppressWarnings("unchecked") default DyadicFunction diMapL(MonadicFunction fn) { - return (DyadicFunction) ProFunctor.super.diMapL(fn); + return (DyadicFunction) Profunctor.super.diMapL(fn); } @Override + @SuppressWarnings("unchecked") default DyadicFunction diMapR(MonadicFunction fn) { - return (DyadicFunction) ProFunctor.super.diMapR(fn); + return (DyadicFunction) Profunctor.super.diMapR(fn); } @Override diff --git a/src/main/java/com/jnape/palatable/lambda/functions/MonadicFunction.java b/src/main/java/com/jnape/palatable/lambda/functions/MonadicFunction.java index 2668dd0fd..dddd2ec8c 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/MonadicFunction.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/MonadicFunction.java @@ -1,9 +1,18 @@ package com.jnape.palatable.lambda.functions; -import com.jnape.palatable.lambda.applicative.Functor; +import com.jnape.palatable.lambda.functor.Functor; +import java.util.function.Function; + +/** + * A function taking a single argument. This is the core function type that all other function types extend and + * auto-curry with. + * + * @param The input type + * @param The output type + */ @FunctionalInterface -public interface MonadicFunction extends Functor { +public interface MonadicFunction extends Functor, Function { B apply(A a); @@ -15,4 +24,14 @@ default MonadicFunction then(MonadicFunction g default MonadicFunction fmap(MonadicFunction g) { return a -> g.apply(apply(a)); } + + @Override + default MonadicFunction compose(Function before) { + return z -> apply(before.apply(z)); + } + + @Override + default MonadicFunction andThen(Function after) { + return a -> after.apply(apply(a)); + } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/TriadicFunction.java b/src/main/java/com/jnape/palatable/lambda/functions/TriadicFunction.java index bafcdf730..db3cb325d 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/TriadicFunction.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/TriadicFunction.java @@ -1,7 +1,17 @@ package com.jnape.palatable.lambda.functions; -import com.jnape.palatable.lambda.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; +/** + * A function taking three arguments. Auto-curried + * + * @param The first argument type + * @param The second argument type + * @param The third argument type + * @param The return type + * @see DyadicFunction + * @see com.jnape.palatable.lambda.functions.builtin.dyadic.Partial3 + */ @FunctionalInterface public interface TriadicFunction extends DyadicFunction> { diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/All.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/All.java index d722ef60e..768a76b0b 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/All.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/All.java @@ -3,10 +3,21 @@ import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; +/** + * Eagerly apply a predicate to each element in an Iterable, returning true if every element + * satisfies the predicate, and false otherwise. This method short-circuits on the first false + * evaluation. + * + * @param The input Iterable element type + * @see Any + */ public final class All implements DyadicFunction, Iterable, Boolean> { + private All() { + } + @Override - public final Boolean apply(MonadicFunction predicate, Iterable as) { + public Boolean apply(MonadicFunction predicate, Iterable as) { for (A a : as) if (!predicate.apply(a)) return false; @@ -23,6 +34,6 @@ public static MonadicFunction, Boolean> all(MonadicFunction boolean all(MonadicFunction predicate, Iterable as) { - return all(predicate).apply(as); + return All.all(predicate).apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Any.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Any.java index 87251cb1c..110111adb 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Any.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Any.java @@ -3,10 +3,21 @@ import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; +/** + * Eagerly apply a predicate to each element in an Iterable, returning true if any element + * satisfies the predicate, and false otherwise. This method short-circuits on the first true + * evaluation. + * + * @param The input Iterable element type + * @see All + */ public final class Any implements DyadicFunction, Iterable, Boolean> { + private Any() { + } + @Override - public final Boolean apply(MonadicFunction predicate, Iterable as) { + public Boolean apply(MonadicFunction predicate, Iterable as) { for (A a : as) if (predicate.apply(a)) return true; @@ -23,6 +34,6 @@ public static MonadicFunction, Boolean> any(MonadicFunction Boolean any(MonadicFunction predicate, Iterable as) { - return any(predicate).apply(as); + return Any.any(predicate).apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java index ce76c57d5..159e1f8b6 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProduct.java @@ -1,14 +1,31 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.CombinatorialIterator; -import com.jnape.palatable.lambda.tuples.Tuple2; +/** + * Lazily compute the cartesian product of an Iterable<A> and Iterable<B>, + * returning an Iterable<Tuple2<A, B>>, the products as tuples of + * multiplicand As and multiplier Bs. + *

+ * Note that this algorithm exhaustively pairs all elements from Iterable<B> to the first element of + * Iterable<A> before advancing to the next element of Iterable<A>, so if + * Iterable<B> is infinite, only one element from Iterable<A> will ever be + * paired. + * + * @param The multiplicand Iterable element type + * @param The multiplier Iterable element type + * @see Zip + */ public final class CartesianProduct implements DyadicFunction, Iterable, Iterable>> { + private CartesianProduct() { + } + @Override - public final Iterable> apply(final Iterable as, final Iterable bs) { + public Iterable> apply(Iterable as, Iterable bs) { return () -> new CombinatorialIterator<>(as.iterator(), bs.iterator()); } @@ -20,7 +37,7 @@ public static MonadicFunction, Iterable>> cartes return CartesianProduct.cartesianProduct().apply(as); } - public static Iterable> cartesianProduct(final Iterable as, final Iterable bs) { + public static Iterable> cartesianProduct(Iterable as, Iterable bs) { return CartesianProduct.cartesianProduct(as).apply(bs); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Drop.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Drop.java index ffc02e099..537fa015e 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Drop.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Drop.java @@ -4,10 +4,22 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.DroppingIterator; +/** + * Lazily skip the first n elements from an Iterable by returning an Iterable + * that begins iteration after the nth element. If n is greater than or equal to the length of + * the Iterable, an empty Iterable is returned. + * + * @param The Iterable element type + * @see DropWhile + * @see Take + */ public final class Drop implements DyadicFunction, Iterable> { + private Drop() { + } + @Override - public final Iterable apply(final Integer n, final Iterable as) { + public Iterable apply(Integer n, Iterable as) { return () -> new DroppingIterator<>(n, as.iterator()); } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhile.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhile.java index 1b12aae0e..676eef969 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhile.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhile.java @@ -4,9 +4,23 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.PredicatedDroppingIterator; +/** + * Lazily limit the Iterable by skipping the first contiguous group of elements that satisfy the predicate, + * beginning iteration at the first element for which the predicate evaluates to false. + * + * @param The Iterable element type + * @see Drop + * @see Filter + * @see TakeWhile + */ + public final class DropWhile implements DyadicFunction, Iterable, Iterable> { + + private DropWhile() { + } + @Override - public final Iterable apply(final MonadicFunction predicate, final Iterable as) { + public Iterable apply(MonadicFunction predicate, Iterable as) { return () -> new PredicatedDroppingIterator<>(predicate, as.iterator()); } @@ -20,7 +34,6 @@ public static MonadicFunction, Iterable> dropWhile( } public static Iterable dropWhile(MonadicFunction predicate, Iterable as) { - return dropWhile(predicate).apply(as); + return DropWhile.dropWhile(predicate).apply(as); } - } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Filter.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Filter.java index e40a63784..394faf487 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Filter.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Filter.java @@ -4,10 +4,21 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.FilteringIterator; +/** + * Lazily apply a predicate to each element in an Iterable, returning an Iterable of just the + * elements for which the predicate evaluated to true. + * + * @param A type contravariant to the input Iterable element type + * @see TakeWhile + * @see DropWhile + */ public final class Filter implements DyadicFunction, Iterable, Iterable> { + private Filter() { + } + @Override - public final Iterable apply(final MonadicFunction predicate, final Iterable as) { + public Iterable apply(MonadicFunction predicate, Iterable as) { return () -> new FilteringIterator<>(predicate, as.iterator()); } @@ -15,12 +26,11 @@ public static Filter filter() { return new Filter<>(); } - public static MonadicFunction, Iterable> filter( - final MonadicFunction predicate) { + public static MonadicFunction, Iterable> filter(MonadicFunction predicate) { return Filter.filter().apply(predicate); } - public static Iterable filter(final MonadicFunction predicate, final Iterable as) { - return filter(predicate).apply(as); + public static Iterable filter(MonadicFunction predicate, Iterable as) { + return Filter.filter(predicate).apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/InGroupsOf.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/InGroupsOf.java index 6edb9d831..f2438aead 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/InGroupsOf.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/InGroupsOf.java @@ -4,10 +4,21 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.GroupingIterator; +/** + * Lazily group the Iterable by returning an Iterable of smaller Iterables of + * size k. Note that groups are not padded; that is, if k >= n, where + * n is the number of remaining elements, the final Iterable will have only n + * elements. + * + * @param The Iterable element type + */ public final class InGroupsOf implements DyadicFunction, Iterable>> { + private InGroupsOf() { + } + @Override - public final Iterable> apply(final Integer k, final Iterable as) { + public Iterable> apply(Integer k, Iterable as) { return () -> new GroupingIterator<>(k, as.iterator()); } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java index 2fcdc44d9..22e2a169a 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Iterate.java @@ -6,12 +6,22 @@ import java.util.Optional; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; - +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; + +/** + * Lazily generate an infinite Iterable from the successive applications of the function first to the + * initial seed value, then to the result, and so on; i.e., the result of iterate(x -> x + 1, 0) would + * produce an infinite Iterable over the elements 0, 1, 2, 3, ... and so on. + * + * @param The Iterable element type + */ public final class Iterate implements DyadicFunction, A, Iterable> { + private Iterate() { + } + @Override - public final Iterable apply(final MonadicFunction fn, final A seed) { + public Iterable apply(MonadicFunction fn, A seed) { return () -> new UnfoldingIterator<>(a -> Optional.of(tuple(a, fn.apply(a))), seed); } @@ -24,6 +34,6 @@ public static MonadicFunction> iterate(MonadicFunction Iterable iterate(MonadicFunction fn, A seed) { - return iterate(fn).apply(seed); + return Iterate.iterate(fn).apply(seed); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Map.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Map.java index c64b9202a..47e77e3b8 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Map.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Map.java @@ -4,10 +4,20 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.MappingIterator; +/** + * Lazily apply a function to each element in an Iterable, producing an Iterable of the mapped + * results. + * + * @param A type contravariant to the input Iterable element type + * @param A type covariant to the output Iterable element type + */ public final class Map implements DyadicFunction, Iterable, Iterable> { + private Map() { + } + @Override - public final Iterable apply(final MonadicFunction function, final Iterable as) { + public Iterable apply(MonadicFunction function, Iterable as) { return () -> new MappingIterator<>(function, as.iterator()); } @@ -21,6 +31,6 @@ public static MonadicFunction, Iterable> map( } public static Iterable map(MonadicFunction function, Iterable as) { - return map(function).apply(as); + return Map.map(function).apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial2.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial2.java index 6ba8c94a4..2990dac51 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial2.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial2.java @@ -3,8 +3,20 @@ import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; +/** + * Partially apply (fix) the first argument of a DyadicFunction, producing a MonadicFunction + * that takes the remaining argument. This is isomorphic to calling {@link com.jnape.palatable.lambda.functions.DyadicFunction#apply(Object)}. + * + * @param The type of the value to be supplied + * @param The input argument type of the resulting function + * @param The return type of the resulting function + * @see Partial3 + */ public final class Partial2 implements DyadicFunction, A, MonadicFunction> { + private Partial2() { + } + @Override public MonadicFunction apply(DyadicFunction function, A a) { return function.apply(a); @@ -18,7 +30,7 @@ public static MonadicFunction> partial2(Dyadi return Partial2.partial2().apply(new Partial2<>(), function); } - public static MonadicFunction partial2(DyadicFunction function, final A a) { + public static MonadicFunction partial2(DyadicFunction function, A a) { return partial2(function).apply(a); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial3.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial3.java index 9bdf0c71c..cb1423b01 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial3.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Partial3.java @@ -4,8 +4,21 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.functions.TriadicFunction; +/** + * Partially apply (fix) the first argument of a TriadicFunction, producing a DyadicFunction + * that takes the remaining two argument. This is isomorphic to calling {@link com.jnape.palatable.lambda.functions.TriadicFunction#apply(Object)}. + * + * @param The type of the value to be supplied + * @param The first input argument type of the resulting function + * @param The second input argument type of the resulting function + * @param The return type of the resulting function + * @see Partial2 + */ public final class Partial3 implements DyadicFunction, A, DyadicFunction> { + private Partial3() { + } + @Override public DyadicFunction apply(TriadicFunction function, A a) { return function.apply(a); diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeft.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeft.java index bdebfedde..40615afbb 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeft.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeft.java @@ -1,35 +1,52 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; -import com.jnape.palatable.lambda.exceptions.EmptyIterableException; import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; import java.util.Iterator; +import java.util.Optional; import static com.jnape.palatable.lambda.functions.builtin.triadic.FoldLeft.foldLeft; -public final class ReduceLeft implements DyadicFunction, Iterable, A> { +/** + * Given an Iterable of As and a {@link DyadicFunction}<A, A, A>, + * iteratively accumulate over the Iterable, returning an Optional<A> (if the + * Iterable is empty, the result is Optional.empty(); otherwise, the result is wrapped in + * Optional.of(). For this reason, null accumulation results are considered erroneous and will + * throw. + *

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

+ * This function is isomorphic to a right fold over the Iterable where the tail element is the starting + * accumulation value and the result is lifted into an Optional. + * + * @param The input Iterable element type, as well as the accumulation type + * @see ReduceLeft + * @see com.jnape.palatable.lambda.functions.builtin.triadic.FoldRight + */ +public final class ReduceRight implements DyadicFunction, Iterable, Optional> { + + private ReduceRight() { + } @Override - public final A apply(DyadicFunction function, Iterable as) { + public final Optional apply(DyadicFunction function, Iterable as) { return reduceLeft(function.flip(), reverse(as)); } @@ -17,12 +37,13 @@ public static ReduceRight reduceRight() { return new ReduceRight<>(); } - public static MonadicFunction, A> reduceRight( + public static MonadicFunction, Optional> reduceRight( DyadicFunction function) { return ReduceRight.reduceRight().apply(function); } - public static A reduceRight(DyadicFunction function, Iterable as) { - return reduceRight(function).apply(as); + public static Optional reduceRight(DyadicFunction function, + Iterable as) { + return ReduceRight.reduceRight(function).apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Take.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Take.java index aab3a81bb..c0b860ca1 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Take.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Take.java @@ -4,10 +4,22 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.TakingIterator; +/** + * Lazily limit the Iterable to n elements by returning an Iterable that stops + * iteration after the nth element, or the last element of the Iterable, whichever comes + * first. + * + * @param The Iterable element type + * @see TakeWhile + * @see Drop + */ public final class Take implements DyadicFunction, Iterable> { + private Take() { + } + @Override - public final Iterable apply(final Integer n, final Iterable as) { + public Iterable apply(Integer n, Iterable as) { return () -> new TakingIterator<>(n, as.iterator()); } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhile.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhile.java index a4ea0b5c3..335983e6e 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhile.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhile.java @@ -4,10 +4,22 @@ import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.PredicatedTakingIterator; +/** + * Lazily limit the Iterable to the first group of contiguous elements that satisfy the predicate by + * iterating up to, but not including, the first element for which the predicate evaluates to false. + * + * @param The Iterable element type + * @see Take + * @see Filter + * @see DropWhile + */ public final class TakeWhile implements DyadicFunction, Iterable, Iterable> { + private TakeWhile() { + } + @Override - public final Iterable apply(final MonadicFunction predicate, final Iterable as) { + public Iterable apply(MonadicFunction predicate, Iterable as) { return () -> new PredicatedTakingIterator<>(predicate, as.iterator()); } @@ -21,6 +33,6 @@ public static MonadicFunction, Iterable> takeWhile( } public static Iterable takeWhile(MonadicFunction predicate, Iterable as) { - return takeWhile(predicate).apply(as); + return TakeWhile.takeWhile(predicate).apply(as); } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java index 654eb3971..6a512a16e 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2.java @@ -1,18 +1,37 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.functions.DyadicFunction; -import com.jnape.palatable.lambda.tuples.Tuple2; +import com.jnape.palatable.lambda.functions.MonadicFunction; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; +/** + * Creates a Tuple2 from two values. + * + * @param The type of the first value; also the first slot type of returned Tuple2 + * @param The type of the second value; also the second slot type of returned Tuple2 + * @see Tuple2 + */ public final class Tupler2 implements DyadicFunction> { + private Tupler2() { + } + @Override - public final Tuple2 apply(A a, B b) { + public Tuple2 apply(A a, B b) { return tuple(a, b); } public static Tupler2 tupler() { return new Tupler2<>(); } + + public static MonadicFunction> tupler(A a) { + return Tupler2.tupler().apply(a); + } + + public static Tuple2 tupler(A a, B b) { + return Tupler2.tupler(a).apply(b); + } } diff --git a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java index 0b82cbfed..3a87da9ea 100644 --- a/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java +++ b/src/main/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Unfoldr.java @@ -1,14 +1,38 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; import com.jnape.palatable.lambda.iterators.UnfoldingIterator; -import com.jnape.palatable.lambda.tuples.Tuple2; import java.util.Optional; +/** + * Given an initial seed value and a function that takes the seed type and produces an Optional<{@link + * Tuple2}<X, Seed>>, where the tuple's first slot represents the next Iterable element, + * and the second slot represents the next input to the unfolding function, unfold an Iterable of + * Xs. Returning Optional.empty() from the unfolding function is a signal that the + * Iterable is fully unfolded. + *

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

+ * Example: + *

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

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

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

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

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

+ * For more information, read about Profunctors. + * + * @param The type of the first parameter + * @param The type of the second parameter + * @see Functor + * @see Bifunctor + * @see com.jnape.palatable.lambda.functions.DyadicFunction + */ +@FunctionalInterface +public interface Profunctor { + + default Profunctor diMapL(MonadicFunction fn) { + return diMap(fn, id()); + } + + default Profunctor diMapR(MonadicFunction fn) { + return diMap(id(), fn); + } + + Profunctor diMap(MonadicFunction f1, + MonadicFunction f2); +} diff --git a/src/main/java/com/jnape/palatable/lambda/iterators/CombinatorialIterator.java b/src/main/java/com/jnape/palatable/lambda/iterators/CombinatorialIterator.java index c072fabc1..526cbd8c9 100644 --- a/src/main/java/com/jnape/palatable/lambda/iterators/CombinatorialIterator.java +++ b/src/main/java/com/jnape/palatable/lambda/iterators/CombinatorialIterator.java @@ -1,12 +1,12 @@ package com.jnape.palatable.lambda.iterators; -import com.jnape.palatable.lambda.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import java.util.ArrayList; import java.util.Iterator; import java.util.ListIterator; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; public class CombinatorialIterator extends ImmutableIterator> { private final Iterator asIterator; diff --git a/src/main/java/com/jnape/palatable/lambda/iterators/PredicatedTakingIterator.java b/src/main/java/com/jnape/palatable/lambda/iterators/PredicatedTakingIterator.java index 1dbd5c29b..eb9bc32a8 100644 --- a/src/main/java/com/jnape/palatable/lambda/iterators/PredicatedTakingIterator.java +++ b/src/main/java/com/jnape/palatable/lambda/iterators/PredicatedTakingIterator.java @@ -7,7 +7,7 @@ public class PredicatedTakingIterator extends ImmutableIterator { private final MonadicFunction predicate; - private final RewindableIterator rewindableIterator; + private final RewindableIterator rewindableIterator; private boolean stillTaking; public PredicatedTakingIterator(MonadicFunction predicate, diff --git a/src/main/java/com/jnape/palatable/lambda/iterators/RewindableIterator.java b/src/main/java/com/jnape/palatable/lambda/iterators/RewindableIterator.java index e9a5c616f..67529fbac 100644 --- a/src/main/java/com/jnape/palatable/lambda/iterators/RewindableIterator.java +++ b/src/main/java/com/jnape/palatable/lambda/iterators/RewindableIterator.java @@ -5,36 +5,9 @@ public class RewindableIterator extends ImmutableIterator { - private static class Cache { - - private A cache; - - public void store(A a) { - cache = a; - } - - public A retrieve() { - if (cache == null) - throw new NoSuchElementException("Cache is empty."); - - A cache = this.cache; - this.cache = null; - return cache; - } - - public boolean isEmpty() { - return cache == null; - } - - public boolean isNotEmpty() { - return !isEmpty(); - } - } - private final Iterator asIterator; private final Cache cache; private boolean rewound; - public RewindableIterator(Iterator asIterator) { this.asIterator = asIterator; cache = new Cache<>(); @@ -67,4 +40,30 @@ public void rewind() { public boolean isRewound() { return rewound; } + + private static class Cache { + + private A cache; + + public void store(A a) { + cache = a; + } + + public A retrieve() { + if (cache == null) + throw new NoSuchElementException("Cache is empty."); + + A cache = this.cache; + this.cache = null; + return cache; + } + + public boolean isEmpty() { + return cache == null; + } + + public boolean isNotEmpty() { + return !isEmpty(); + } + } } diff --git a/src/main/java/com/jnape/palatable/lambda/iterators/ScanningIterator.java b/src/main/java/com/jnape/palatable/lambda/iterators/ScanningIterator.java new file mode 100644 index 000000000..085ba592c --- /dev/null +++ b/src/main/java/com/jnape/palatable/lambda/iterators/ScanningIterator.java @@ -0,0 +1,35 @@ +package com.jnape.palatable.lambda.iterators; + +import com.jnape.palatable.lambda.functions.DyadicFunction; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +public final class ScanningIterator extends ImmutableIterator { + + private final DyadicFunction scanner; + private final Iterator asIterator; + private B b; + + public ScanningIterator(DyadicFunction scanner, B b, + Iterator asIterator) { + this.scanner = scanner; + this.b = b; + this.asIterator = asIterator; + } + + @Override + public boolean hasNext() { + return b != null; + } + + @Override + public B next() { + if (b == null) + throw new NoSuchElementException(); + + B next = b; + b = asIterator.hasNext() ? scanner.apply(b, asIterator.next()) : null; + return next; + } +} diff --git a/src/main/java/com/jnape/palatable/lambda/iterators/UnfoldingIterator.java b/src/main/java/com/jnape/palatable/lambda/iterators/UnfoldingIterator.java index 7939716e7..3b34b9ed7 100644 --- a/src/main/java/com/jnape/palatable/lambda/iterators/UnfoldingIterator.java +++ b/src/main/java/com/jnape/palatable/lambda/iterators/UnfoldingIterator.java @@ -1,7 +1,7 @@ package com.jnape.palatable.lambda.iterators; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.functions.MonadicFunction; -import com.jnape.palatable.lambda.tuples.Tuple2; import java.util.NoSuchElementException; import java.util.Optional; diff --git a/src/main/java/com/jnape/palatable/lambda/tuples/Tuple2.java b/src/main/java/com/jnape/palatable/lambda/tuples/Tuple2.java deleted file mode 100644 index bda5ade0a..000000000 --- a/src/main/java/com/jnape/palatable/lambda/tuples/Tuple2.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.jnape.palatable.lambda.tuples; - -import com.jnape.palatable.lambda.applicative.BiFunctor; -import com.jnape.palatable.lambda.functions.MonadicFunction; - -import static java.lang.String.format; - -public class Tuple2<_1, _2> implements BiFunctor<_1, _2> { - - public final _1 _1; - public final _2 _2; - - public Tuple2(_1 _1, _2 _2) { - this._1 = _1; - this._2 = _2; - } - - @Override - public Tuple2 biMap(MonadicFunction f1, - MonadicFunction f2) { - return tuple(f1.apply(_1), f2.apply(_2)); - } - - @Override - public Tuple2 biMapL(MonadicFunction fn) { - return (Tuple2) BiFunctor.super.biMapL(fn); - } - - @Override - public Tuple2<_1, C> biMapR(MonadicFunction fn) { - return (Tuple2<_1, C>) BiFunctor.super.biMapR(fn); - } - - @Override - public String toString() { - return format("(%s, %s)", _1, _2); - } - - @Override - public boolean equals(Object other) { - if (other instanceof Tuple2) { - Tuple2 that = (Tuple2) other; - return this._1.equals(that._1) && this._2.equals(that._2); - } - return false; - } - - @Override - public int hashCode() { - int result = _1.hashCode(); - return 31 * result + _2.hashCode(); - } - - public static <_1, _2> Tuple2<_1, _2> tuple(_1 _1, _2 _2) { - return new Tuple2<>(_1, _2); - } -} diff --git a/src/main/java/com/jnape/palatable/lambda/tuples/Tuple3.java b/src/main/java/com/jnape/palatable/lambda/tuples/Tuple3.java deleted file mode 100644 index 54c9d1ea3..000000000 --- a/src/main/java/com/jnape/palatable/lambda/tuples/Tuple3.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.jnape.palatable.lambda.tuples; - -import static java.lang.String.format; - -public class Tuple3<_1, _2, _3> extends Tuple2<_1, _2> { - - public final _3 _3; - - public Tuple3(_1 _1, _2 _2, _3 _3) { - super(_1, _2); - this._3 = _3; - } - - @Override - public String toString() { - return format("(%s, %s, %s)", _1, _2, _3); - } - - public static <_1, _2, _3> Tuple3<_1, _2, _3> tuple(_1 _1, _2 _2, _3 _3) { - return new Tuple3<>(_1, _2, _3); - } -} diff --git a/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java b/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java new file mode 100644 index 000000000..2950ed5b0 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/EitherTest.java @@ -0,0 +1,161 @@ +package com.jnape.palatable.lambda.adt; + +import com.jnape.palatable.lambda.functions.DyadicFunction; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.util.Optional; + +import static com.jnape.palatable.lambda.adt.Either.fromOptional; +import static com.jnape.palatable.lambda.adt.Either.left; +import static com.jnape.palatable.lambda.adt.Either.right; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +public class EitherTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void recoverLiftsLeftAndFlattensRight() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.recover(l -> -1), is(-1)); + assertThat(right.recover(l -> -1), is(1)); + } + + @Test + public void forfeitLiftsRightAndFlattensLeft() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.forfeit(r -> "bar"), is("foo")); + assertThat(right.forfeit(r -> "bar"), is("bar")); + } + + @Test + public void orReplacesLeftAndFlattensRight() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.or(-1), is(-1)); + assertThat(right.or(-1), is(1)); + } + + @Test + public void orThrowFlattensRightOrThrowsException() { + Either left = left("foo"); + Either right = right(1); + + assertThat(right.orThrow(IllegalStateException::new), is(1)); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("foo"); + + left.orThrow(IllegalStateException::new); + } + + @Test + public void filterLiftsRight() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.filter(x -> true, () -> "bar"), is(left)); + assertThat(left.filter(x -> false, () -> "bar"), is(left)); + assertThat(right.filter(x -> true, () -> "bar"), is(right)); + assertThat(right.filter(x -> false, () -> "bar"), is(left("bar"))); + } + + @Test + public void monadicFlatMapLiftsRightAndFlattensBackToEither() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.flatMap(r -> right(r + 1)), is(left("foo"))); + assertThat(right.flatMap(r -> right(r + 1)), is(right(2))); + } + + @Test + public void dyadicFlatMapDuallyLiftsAndFlattensBackToEither() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.flatMap(l -> left(l + "bar"), r -> right(r + 1)), is(left("foobar"))); + assertThat(right.flatMap(l -> left(l + "bar"), r -> right(r + 1)), is(right(2))); + } + + @Test + public void mergeDuallyLiftsAndCombinesBiasingLeft() { + Either left1 = left("foo"); + Either right1 = right(1); + + Either left2 = left("bar"); + Either right2 = right(2); + + DyadicFunction concat = String::concat; + DyadicFunction add = (r1, r2) -> r1 + r2; + + assertThat(left1.merge(concat, add, left2), is(left("foobar"))); + assertThat(left1.merge(concat, add, right2), is(left1)); + assertThat(right1.merge(concat, add, left2), is(left2)); + assertThat(right1.merge(concat, add, right1), is(right(2))); + } + + @Test + public void matchDuallyLiftsAndFlattens() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.match(l -> l + "bar", r -> r + 1), is("foobar")); + assertThat(right.match(l -> l + "bar", r -> r + 1), is(2)); + } + + @Test + public void fromOptionalMapsOptionalToEither() { + Optional present = Optional.of("foo"); + Optional absent = Optional.empty(); + + assertThat(fromOptional(present, () -> -1), is(right("foo"))); + assertThat(fromOptional(absent, () -> -1), is(left(-1))); + } + + @Test + public void functorialProperties() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.fmap(r -> r + 1), is(left)); + assertThat(right.fmap(r -> r + 1), is(right(2))); + } + + @Test + public void biFunctorialProperties() { + Either left = left("foo"); + Either right = right(1); + + assertThat(left.biMap(l -> l + "bar", r -> r + 1), is(left("foobar"))); + assertThat(right.biMap(l -> l + "bar", r -> r + 1), is(right(2))); + } + + @Test + public void monadicTryingLiftsCheckedSupplier() { + assertEquals(right(1), Either.trying(() -> 1)); + + Exception checkedException = new Exception("expected"); + assertEquals(left(checkedException), Either.trying(() -> { + throw checkedException; + })); + } + + @Test + public void dyadicTryingLiftsCheckedSupplierMappingAnyThrownExceptions() { + assertEquals(right(1), Either.trying(() -> 1, Throwable::getMessage)); + assertEquals(left("expected"), Either.trying(() -> { + throw new Exception("expected"); + }, Throwable::getMessage)); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/adt/HListTest.java b/src/test/java/com/jnape/palatable/lambda/adt/HListTest.java new file mode 100644 index 000000000..bdac78d81 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/HListTest.java @@ -0,0 +1,44 @@ +package com.jnape.palatable.lambda.adt; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.HList.cons; +import static com.jnape.palatable.lambda.adt.HList.list; +import static com.jnape.palatable.lambda.adt.HList.nil; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +public class HListTest { + + @Test + public void head() { + assertEquals("head", nil().cons("head").head()); + assertEquals("new head", nil().cons("old head").cons("new head").head()); + } + + @Test + public void tail() { + assertEquals(nil(), nil().cons("head").tail()); + assertEquals(nil().cons("old head"), nil().cons("old head").cons("new head").tail()); + } + + @Test + public void convenienceStaticFactoryMethods() { + assertEquals(nil().cons(1), cons(1, nil())); + assertEquals(nil().cons(1), list(1)); + assertEquals(nil().cons('2').cons(1), list(1, '2')); + assertEquals(nil().cons("3").cons('2').cons(1), list(1, '2', "3")); + assertEquals(nil().cons(4.0).cons("3").cons('2').cons(1), list(1, '2', "3", 4.0)); + assertEquals(nil().cons(false).cons(4.0).cons("3").cons('2').cons(1), list(1, '2', "3", 4.0, false)); + } + + @Test + public void functorialProperties() { + assertEquals(list("1"), list(1).fmap(Object::toString)); + } + + @Test + public void nilReusesInstance() { + assertSame(nil(), nil()); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/tuples/Tuple2Test.java b/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple2Test.java similarity index 64% rename from src/test/java/com/jnape/palatable/lambda/tuples/Tuple2Test.java rename to src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple2Test.java index bc390178e..4a183cd5e 100644 --- a/src/test/java/com/jnape/palatable/lambda/tuples/Tuple2Test.java +++ b/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple2Test.java @@ -1,8 +1,8 @@ -package com.jnape.palatable.lambda.tuples; +package com.jnape.palatable.lambda.adt.tuples; import org.junit.Test; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; @@ -12,36 +12,12 @@ public class Tuple2Test { @Test public void hasTwoSlots() { - Tuple2 stringIntTuple = new Tuple2<>("foo", 1); + Tuple2 stringIntTuple = tuple("foo", 1); assertThat(stringIntTuple._1, is("foo")); assertThat(stringIntTuple._2, is(1)); } - @Test - public void mapsCovariantlyOverOverLeftSlot() { - assertThat( - tuple("foo", 1).biMapL(String::toUpperCase), - is(tuple("FOO", 1)) - ); - } - - @Test - public void mapsCovariantlyOverRightSlot() { - assertThat( - tuple("foo", 1).biMapR(String::valueOf), - is(tuple("foo", "1")) - ); - } - - @Test - public void mapsOverBothSlotsInSingleTransformation() { - assertThat( - tuple("foo", 1).biMap(String::toUpperCase, i -> i + 1), - is(tuple("FOO", 2)) - ); - } - @Test public void toStringIsReasonable() { assertThat(tuple("1", "2").toString(), is("(1, 2)")); @@ -73,4 +49,16 @@ public void hashesCorrectlyForEqualTuples() { public void hashesUsingReasonableDistribution() { assertNotEquals(tuple("foo", 1).hashCode(), tuple("bar", 2).hashCode()); } + + @Test + public void functorialProperties() { + assertThat(tuple("foo", 1).fmap(String::valueOf), is(tuple("foo", "1"))); + } + + @Test + public void biFunctorialProperties() { + assertThat(tuple("foo", 1).biMapL(String::toUpperCase), is(tuple("FOO", 1))); + assertThat(tuple("foo", 1).biMapR(String::valueOf), is(tuple("foo", "1"))); + assertThat(tuple("foo", 1).biMap(String::toUpperCase, i -> i + 1), is(tuple("FOO", 2))); + } } diff --git a/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple3Test.java b/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple3Test.java new file mode 100644 index 000000000..e1cfc07bf --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/adt/tuples/Tuple3Test.java @@ -0,0 +1,53 @@ +package com.jnape.palatable.lambda.adt.tuples; + +import org.junit.Test; + +import static com.jnape.palatable.lambda.adt.tuples.Tuple3.tuple; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertThat; + +public class Tuple3Test { + + @Test + public void hasThreeSlots() { + Tuple3 integerStringCharacterTuple = tuple(1, "two", '3'); + + assertThat(integerStringCharacterTuple._1, is(1)); + assertThat(integerStringCharacterTuple._2, is("two")); + assertThat(integerStringCharacterTuple._3, is('3')); + } + + @Test + public void usesValueBasedEquality() { + Tuple3 same = tuple("a", "b", 1); + Tuple3 alsoSame = tuple("a", "b", 1); + Tuple3 different = tuple("b", "b", 2); + + assertEquals(same, alsoSame); + assertEquals(alsoSame, same); + + assertNotEquals(same, different); + assertNotEquals(alsoSame, different); + assertNotEquals(different, same); + assertNotEquals(different, alsoSame); + + assertNotEquals(same, new Object()); + } + + @Test + public void hashesCorrectlyForEqualTuples() { + assertEquals(tuple("foo", "bar", 1).hashCode(), tuple("foo", "bar", 1).hashCode()); + } + + @Test + public void hashesUsingReasonableDistribution() { + assertNotEquals(tuple("foo", "bar", 1).hashCode(), tuple("bar", "baz", 2).hashCode()); + } + + @Test + public void toStringIsReasonable() { + assertThat(tuple("foo", "bar", "baz").toString(), is("(foo, bar, baz)")); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationIteratorTest.java b/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationIteratorTest.java deleted file mode 100644 index 829193af8..000000000 --- a/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationIteratorTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.jnape.palatable.lambda.continuation; - -import org.junit.Test; - -import java.util.NoSuchElementException; -import java.util.Optional; - -import static com.jnape.palatable.lambda.continuation.Continuations.continuing; -import static org.hamcrest.MatcherAssert.assertThat; -import static testsupport.matchers.IterableMatcher.iterates; - -public class ContinuationIteratorTest { - - @Test - public void iteratesContinuation() { - Continuation numbers = continuing(1, 2, 3); - ContinuationIterator continuationIterator = new ContinuationIterator<>(numbers); - - assertThat(() -> continuationIterator, iterates(1, 2, 3)); - } - - @Test(expected = NoSuchElementException.class) - public void usesStandardSemanticFailuresIfIteratingPastLastContinuation() { - new ContinuationIterator(Optional::empty).next(); - } -} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationTest.java b/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationTest.java deleted file mode 100644 index d9b16a667..000000000 --- a/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.jnape.palatable.lambda.continuation; - -import org.junit.Test; - -import java.util.Optional; -import java.util.concurrent.atomic.AtomicInteger; - -import static com.jnape.palatable.lambda.continuation.Continuations.continuing; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static testsupport.matchers.IterableMatcher.iterates; - -public class ContinuationTest { - - private static final Continuation ONE_TWO_THREE = continuing(1, 2, 3); - - @Test - public void isIterable() { - assertThat(ONE_TWO_THREE, iterates(1, 2, 3)); - } - - @Test - public void fmapRecursivelyComposesContinuationLineage() { - assertThat(ONE_TWO_THREE.fmap(Object::toString), iterates("1", "2", "3")); - } - - @Test - public void thenContinuesOnToNextContinuationOnceEmpty() { - Continuation fourFiveSix = continuing(4, 5, 6); - assertThat(ONE_TWO_THREE.then(fourFiveSix), iterates(1, 2, 3, 4, 5, 6)); - } - - @Test - public void memoizesEveryResult() { - AtomicInteger index = new AtomicInteger(0); - - Continuation evilSideEffectDrivenContinuation = () -> - Optional.of(tuple(index.incrementAndGet(), () -> - Optional.of(tuple(index.incrementAndGet(), Optional::empty)))); - - Continuation memoizingContinuation = evilSideEffectDrivenContinuation.memoized(); - - assertThat(memoizingContinuation, iterates(1, 2)); - assertThat(memoizingContinuation, iterates(1, 2)); - assertThat(memoizingContinuation, iterates(1, 2)); - } - - @Test - public void memoizationIsLazy() { - AtomicInteger invocations = new AtomicInteger(0); - Continuation invocationCountingContinuation = continuing(1, 2, 3, 4, 5).fmap(i -> { - invocations.incrementAndGet(); - return i; - }); - - invocationCountingContinuation.memoized(); - - assertThat(invocations.get(), is(0)); - } -} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationsTest.java b/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationsTest.java deleted file mode 100644 index 6229b2f6d..000000000 --- a/src/test/java/com/jnape/palatable/lambda/continuation/ContinuationsTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.jnape.palatable.lambda.continuation; - -import org.junit.Test; - -import java.lang.reflect.Constructor; -import java.util.stream.Stream; - -import static com.jnape.palatable.lambda.continuation.Continuations.continuing; -import static java.lang.reflect.Modifier.isPrivate; -import static java.util.Arrays.asList; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static testsupport.matchers.IterableMatcher.iterates; - -public class ContinuationsTest { - - @Test - public void cannotBeInstantiated() { - Constructor[] constructors = Continuations.class.getDeclaredConstructors(); - assertThat(constructors.length, is(1)); - assertTrue(isPrivate(constructors[0].getModifiers())); - } - - @Test - public void continuingVarargs() { - assertThat(continuing(1, 2, 3), iterates(1, 2, 3)); - } - - @Test - public void continuingStreams() { - Stream stream = Stream.builder() - .add(1) - .add(2) - .add(3) - .build(); - assertThat(continuing(stream), iterates(1, 2, 3)); - } - - @Test - public void continuingIterables() { - assertThat(continuing(asList(1, 2, 3)), iterates(1, 2, 3)); - } - - @Test - public void continuingIterators() { - assertThat(continuing(asList(1, 2, 3).iterator()), iterates(1, 2, 3)); - } -} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/continuation/MemoTest.java b/src/test/java/com/jnape/palatable/lambda/continuation/MemoTest.java deleted file mode 100644 index 4d3490bf0..000000000 --- a/src/test/java/com/jnape/palatable/lambda/continuation/MemoTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.jnape.palatable.lambda.continuation; - -import org.junit.Test; -import testsupport.concurrent.Turnstile; - -import java.util.Random; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Supplier; - -import static com.jnape.palatable.lambda.continuation.Memo.memoize; -import static java.lang.Thread.sleep; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; - -public class MemoTest { - - @Test - public void computesValueOnlyOnce() { - AtomicInteger counter = new AtomicInteger(0); - Memo memo = memoize(counter::incrementAndGet); - - assertThat(memo.get(), is(1)); - assertThat(memo.get(), is(1)); - assertThat(memo.get(), is(1)); - assertThat(memo.get(), is(1)); - assertThat(memo.get(), is(1)); - } - - @Test - public void memoizesValuesToo() { - Memo memo = memoize(2); - assertThat(memo.get(), is(2)); - } - - @Test - public void onlyMemoizesValueOfWinningThreadInMultiThreadedRaceSituation() throws InterruptedException { - Turnstile turnstile = new Turnstile(2); - - Supplier randomIntSupplier = () -> { - int i = new Random().nextInt(); - turnstile.arrive(); - return i; - }; - - Memo memo = memoize(randomIntSupplier); - - AtomicInteger thread1Value = new AtomicInteger(); - AtomicInteger thread2Value = new AtomicInteger(); - - Thread thread1 = new Thread(() -> thread1Value.set(memo.get())); - Thread thread2 = new Thread(() -> thread2Value.set(memo.get())); - - thread1.start(); - sleep(100); - thread2.start(); - - thread2.join(); - - assertThat(memo.get(), is(thread1Value.get())); - } -} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/exceptions/EmptyIterableExceptionTest.java b/src/test/java/com/jnape/palatable/lambda/exceptions/EmptyIterableExceptionTest.java deleted file mode 100644 index 657f63bcf..000000000 --- a/src/test/java/com/jnape/palatable/lambda/exceptions/EmptyIterableExceptionTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.jnape.palatable.lambda.exceptions; - -import org.junit.Test; - -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - -public class EmptyIterableExceptionTest { - - @Test - public void betterNotBeChecked() { - assertThat(RuntimeException.class.isInstance(new EmptyIterableException()), is(true)); - } - - @Test - public void hasUsefulMessage() { - assertThat(new EmptyIterableException().getMessage(), is("Iterable was empty.")); - } -} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/DyadicFunctionTest.java b/src/test/java/com/jnape/palatable/lambda/functions/DyadicFunctionTest.java index 945793895..dac5f6999 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/DyadicFunctionTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/DyadicFunctionTest.java @@ -2,7 +2,7 @@ import org.junit.Test; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; @@ -33,7 +33,7 @@ public void mapsContravariantlyOverSecondArgument() { @Test public void mapsCovariantlyOverReturnValue() { - assertThat(CHECK_LENGTH.diMapR(Object::toString).apply("123").apply(3), is("true")); + assertThat(CHECK_LENGTH.diMapR(Object::toString).apply("123").apply(3), is("true")); } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/functions/TriadicFunctionTest.java b/src/test/java/com/jnape/palatable/lambda/functions/TriadicFunctionTest.java index 0f5c2beb0..115e61d0e 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/TriadicFunctionTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/TriadicFunctionTest.java @@ -2,7 +2,7 @@ import org.junit.Test; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/AllTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/AllTest.java index 182755da3..993e4d0a2 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/AllTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/AllTest.java @@ -8,7 +8,7 @@ import testsupport.traits.EmptyIterableSupport; import static com.jnape.palatable.lambda.functions.builtin.dyadic.All.all; -import static com.jnape.palatable.lambda.functions.builtin.monadic.Always.always; +import static com.jnape.palatable.lambda.functions.builtin.monadic.Constantly.constantly; import static com.jnape.palatable.lambda.functions.builtin.monadic.Repeat.repeat; import static java.util.Arrays.asList; import static org.hamcrest.core.Is.is; @@ -21,7 +21,7 @@ public class AllTest { @TestTraits({EmptyIterableSupport.class}) public MonadicFunction, Boolean> createTestSubject() { - return all(always(true)); + return all(constantly(true)); } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/AnyTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/AnyTest.java index 65fd70de5..6ace0734a 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/AnyTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/AnyTest.java @@ -8,7 +8,7 @@ import testsupport.traits.EmptyIterableSupport; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Any.any; -import static com.jnape.palatable.lambda.functions.builtin.monadic.Always.always; +import static com.jnape.palatable.lambda.functions.builtin.monadic.Constantly.constantly; import static com.jnape.palatable.lambda.functions.builtin.monadic.Repeat.repeat; import static java.util.Arrays.asList; import static org.hamcrest.core.Is.is; @@ -21,7 +21,7 @@ public class AnyTest { @TestTraits({EmptyIterableSupport.class}) public MonadicFunction, Boolean> createTestSubject() { - return any(always(true)); + return any(constantly(true)); } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProductTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProductTest.java index baf960bac..af678b616 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProductTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/CartesianProductTest.java @@ -1,7 +1,7 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.functions.MonadicFunction; -import com.jnape.palatable.lambda.tuples.Tuple2; import com.jnape.palatable.traitor.annotations.TestTraits; import com.jnape.palatable.traitor.runners.Traits; import org.junit.Test; @@ -11,8 +11,8 @@ import testsupport.traits.ImmutableIteration; import testsupport.traits.Laziness; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static com.jnape.palatable.lambda.functions.builtin.dyadic.CartesianProduct.cartesianProduct; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; import static java.util.Arrays.asList; import static org.junit.Assert.assertThat; import static testsupport.matchers.IterableMatcher.iterates; diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhileTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhileTest.java index 54807f077..a729ce240 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhileTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/DropWhileTest.java @@ -12,7 +12,7 @@ import testsupport.traits.Laziness; import static com.jnape.palatable.lambda.functions.builtin.dyadic.DropWhile.dropWhile; -import static com.jnape.palatable.lambda.functions.builtin.monadic.Always.always; +import static com.jnape.palatable.lambda.functions.builtin.monadic.Constantly.constantly; import static java.util.Arrays.asList; import static org.junit.Assert.assertThat; import static testsupport.matchers.IterableMatcher.isEmpty; @@ -23,7 +23,7 @@ public class DropWhileTest { @TestTraits({Laziness.class, ImmutableIteration.class, FiniteIteration.class, EmptyIterableSupport.class}) public MonadicFunction, Iterable> createTestSubject() { - return dropWhile(always(true)); + return dropWhile(constantly(true)); } @Test @@ -34,11 +34,11 @@ public void dropsElementsWhilePredicateIsTrue() { @Test public void dropsAllElementsIfPredicateNeverFails() { - assertThat(dropWhile(always(true), asList(1, 2, 3)), isEmpty()); + assertThat(dropWhile(constantly(true), asList(1, 2, 3)), isEmpty()); } @Test public void dropsNoElementsIfPredicateImmediatelyFails() { - assertThat(dropWhile(always(false), asList(1, 2, 3)), iterates(1, 2, 3)); + assertThat(dropWhile(constantly(false), asList(1, 2, 3)), iterates(1, 2, 3)); } } diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/FilterTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/FilterTest.java index 3ad02a9de..bd534cd0c 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/FilterTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/FilterTest.java @@ -12,7 +12,7 @@ import testsupport.traits.Laziness; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Filter.filter; -import static com.jnape.palatable.lambda.functions.builtin.monadic.Always.always; +import static com.jnape.palatable.lambda.functions.builtin.monadic.Constantly.constantly; import static java.util.Arrays.asList; import static org.junit.Assert.assertThat; import static testsupport.matchers.IterableMatcher.iterates; @@ -22,7 +22,7 @@ public class FilterTest { @TestTraits({Laziness.class, EmptyIterableSupport.class, FiniteIteration.class, ImmutableIteration.class}) public MonadicFunction createTraitsTestSubject() { - return filter(always(true)); + return filter(constantly(true)); } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/IterateTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/IterateTest.java index 9f721018b..984794010 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/IterateTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/IterateTest.java @@ -13,7 +13,7 @@ import static com.jnape.palatable.lambda.functions.builtin.dyadic.Iterate.iterate; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Take.take; -import static com.jnape.palatable.lambda.functions.builtin.monadic.Always.always; +import static com.jnape.palatable.lambda.functions.builtin.monadic.Constantly.constantly; import static org.junit.Assert.assertThat; import static testsupport.matchers.IterableMatcher.iterates; @@ -22,7 +22,7 @@ public class IterateTest { @TestTraits({Laziness.class, InfiniteIteration.class, ImmutableIteration.class}) public MonadicFunction createTestSubject() { - return iterate(always(new ArrayList<>())); + return iterate(constantly(new ArrayList<>())); } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeftTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeftTest.java index 1b809f249..53d0bdcd5 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeftTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceLeftTest.java @@ -1,12 +1,12 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; -import com.jnape.palatable.lambda.exceptions.EmptyIterableException; import org.junit.Test; -import java.util.Collections; +import java.util.Optional; import static com.jnape.palatable.lambda.functions.builtin.dyadic.ReduceLeft.reduceLeft; import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static testsupport.functions.ExplainFold.explainFold; @@ -17,12 +17,15 @@ public class ReduceLeftTest { public void reduceLeftAccumulatesLeftToRightUsingFirstElementAsStartingAccumulation() { assertThat( reduceLeft(explainFold(), asList("1", "2", "3", "4", "5")), - is("((((1 + 2) + 3) + 4) + 5)") + is(Optional.of("((((1 + 2) + 3) + 4) + 5)")) ); } - @Test(expected = EmptyIterableException.class) - public void reduceLeftFailsIfEmptyIterable() { - reduceLeft(explainFold(), Collections.emptyList()); + @Test + public void isEmptyIfIterableIsEmpty() { + assertThat( + reduceLeft(explainFold(), emptyList()), + is(Optional.empty()) + ); } } diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceRightTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceRightTest.java index 3103d2694..1f2e0032f 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceRightTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ReduceRightTest.java @@ -2,8 +2,11 @@ import org.junit.Test; +import java.util.Optional; + import static com.jnape.palatable.lambda.functions.builtin.dyadic.ReduceRight.reduceRight; import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static testsupport.functions.ExplainFold.explainFold; @@ -11,10 +14,18 @@ public class ReduceRightTest { @Test - public void reduceRightAccumulatesRightToLeftUsingLastElementAsStartingAccumulation() { + public void accumulatesRightToLeftUsingLastElementAsStartingAccumulation() { assertThat( reduceRight(explainFold(), asList("1", "2", "3", "4", "5")), - is("(1 + (2 + (3 + (4 + 5))))") + is(Optional.of("(1 + (2 + (3 + (4 + 5))))")) + ); + } + + @Test + public void isEmptyIfIterableIsEmpty() { + assertThat( + reduceRight(explainFold(), emptyList()), + is(Optional.empty()) ); } } diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhileTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhileTest.java index 9025adf66..ed75773c0 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhileTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/TakeWhileTest.java @@ -12,7 +12,7 @@ import testsupport.traits.Laziness; import static com.jnape.palatable.lambda.functions.builtin.dyadic.TakeWhile.takeWhile; -import static com.jnape.palatable.lambda.functions.builtin.monadic.Always.always; +import static com.jnape.palatable.lambda.functions.builtin.monadic.Constantly.constantly; import static java.util.Arrays.asList; import static org.junit.Assert.assertThat; import static testsupport.matchers.IterableMatcher.isEmpty; @@ -23,7 +23,7 @@ public class TakeWhileTest { @TestTraits({FiniteIteration.class, EmptyIterableSupport.class, ImmutableIteration.class, Laziness.class}) public MonadicFunction, Iterable> createTestObject() { - return takeWhile(always(true)); + return takeWhile(constantly(true)); } @Test @@ -37,13 +37,13 @@ public void takesElementsWhilePredicateIsTrue() { public void takesAllElementsIfPredicateNeverFails() { String[] requirements = {"fast", "good", "cheap"}; assertThat( - takeWhile(always(true), asList(requirements)), + takeWhile(constantly(true), asList(requirements)), iterates(requirements) ); } @Test public void takesNoElementsIfPredicateImmediatelyFails() { - assertThat(takeWhile(always(false), asList(1, 2, 3)), isEmpty()); + assertThat(takeWhile(constantly(false), asList(1, 2, 3)), isEmpty()); } } diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2Test.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2Test.java index 56c63ccf7..be0eb7a28 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2Test.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/Tupler2Test.java @@ -2,7 +2,8 @@ import org.junit.Test; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.functions.builtin.dyadic.Tupler2.tupler; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; @@ -10,7 +11,6 @@ public class Tupler2Test { @Test public void createsTupleOfTwoThings() { - Tupler2 tupler = new Tupler2<>(); - assertThat(tupler.apply("a", 1), is(tuple("a", 1))); + assertThat(tupler("a", 1), is(tuple("a", 1))); } } diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/UnfoldrTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/UnfoldrTest.java index d14aca74c..7a9722d73 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/UnfoldrTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/UnfoldrTest.java @@ -11,9 +11,9 @@ import java.util.Optional; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Take.take; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Unfoldr.unfoldr; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; import static org.junit.Assert.assertThat; import static testsupport.matchers.IterableMatcher.iterates; @@ -22,7 +22,7 @@ public class UnfoldrTest { @TestTraits({Laziness.class, InfiniteIteration.class, ImmutableIteration.class}) public MonadicFunction createTestSubject() { - return new Unfoldr().apply(x -> Optional.of(tuple(x, x))); + return unfoldr(x -> Optional.of(tuple(x, x))); } @Test diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ZipTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ZipTest.java index aab481abc..5955a417f 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ZipTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/dyadic/ZipTest.java @@ -1,7 +1,7 @@ package com.jnape.palatable.lambda.functions.builtin.dyadic; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.functions.MonadicFunction; -import com.jnape.palatable.lambda.tuples.Tuple2; import com.jnape.palatable.traitor.annotations.TestTraits; import com.jnape.palatable.traitor.runners.Traits; import org.junit.Test; @@ -12,8 +12,8 @@ import java.util.Arrays; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Zip.zip; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; import static java.util.Arrays.asList; import static org.junit.Assert.assertThat; import static testsupport.matchers.IterableMatcher.iterates; diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/AlwaysTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/ConstantlyTest.java similarity index 74% rename from src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/AlwaysTest.java rename to src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/ConstantlyTest.java index 319807b2e..0543c48c3 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/AlwaysTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/ConstantlyTest.java @@ -2,15 +2,15 @@ import org.junit.Test; -import static com.jnape.palatable.lambda.functions.builtin.monadic.Always.always; +import static com.jnape.palatable.lambda.functions.builtin.monadic.Constantly.constantly; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; -public class AlwaysTest { +public class ConstantlyTest { @Test public void returnsConstantOutputRegardlessOfInput() { Object anything = "doesn't matter"; - assertThat(always("yes").apply(anything), is("yes")); + assertThat(constantly("yes").apply(anything), is("yes")); } } diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/ReverseTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/ReverseTest.java index fb289bc6c..53d0e58f2 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/ReverseTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/monadic/ReverseTest.java @@ -14,7 +14,10 @@ import static com.jnape.palatable.lambda.functions.builtin.monadic.Reverse.reverse; import static java.util.Arrays.asList; import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import static testsupport.matchers.IterableMatcher.iterates; @RunWith(Traits.class) diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ScanLeftTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ScanLeftTest.java new file mode 100644 index 000000000..621f94fc4 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ScanLeftTest.java @@ -0,0 +1,42 @@ +package com.jnape.palatable.lambda.functions.builtin.triadic; + +import com.jnape.palatable.lambda.functions.MonadicFunction; +import com.jnape.palatable.traitor.annotations.TestTraits; +import com.jnape.palatable.traitor.runners.Traits; +import org.junit.Test; +import org.junit.runner.RunWith; +import testsupport.traits.EmptyIterableSupport; +import testsupport.traits.FiniteIteration; +import testsupport.traits.Laziness; + +import java.util.Collections; + +import static com.jnape.palatable.lambda.functions.builtin.triadic.ScanLeft.scanLeft; +import static java.util.Arrays.asList; +import static org.junit.Assert.assertThat; +import static testsupport.matchers.IterableMatcher.iterates; + +@RunWith(Traits.class) +public class ScanLeftTest { + + @TestTraits({EmptyIterableSupport.class, FiniteIteration.class, Laziness.class}) + public MonadicFunction, Iterable> createTestSubject() { + return scanLeft((x, y) -> x, new Object()); + } + + @Test + public void buildsUpContinuationOfIncrementalAccumulations() { + assertThat( + scanLeft((x, y) -> x + y, 0, asList(1, 2, 3, 4, 5)), + iterates(0, 1, 3, 6, 10, 15) + ); + } + + @Test + public void initialAccumulationIsOnlyResultIfEmptyIterable() { + assertThat( + scanLeft((x, y) -> x + y, 0, Collections.emptyList()), + iterates(0) + ); + } +} diff --git a/src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ZipWithTest.java b/src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ZipWithTest.java index 4bfa7d147..d9b27f864 100644 --- a/src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ZipWithTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functions/builtin/triadic/ZipWithTest.java @@ -1,8 +1,8 @@ package com.jnape.palatable.lambda.functions.builtin.triadic; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.functions.DyadicFunction; import com.jnape.palatable.lambda.functions.MonadicFunction; -import com.jnape.palatable.lambda.tuples.Tuple2; import com.jnape.palatable.traitor.annotations.TestTraits; import com.jnape.palatable.traitor.runners.Traits; import org.junit.Test; @@ -11,9 +11,9 @@ import testsupport.traits.ImmutableIteration; import testsupport.traits.Laziness; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static com.jnape.palatable.lambda.functions.builtin.dyadic.Zip.zip; import static com.jnape.palatable.lambda.functions.builtin.triadic.ZipWith.zipWith; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; import static java.util.Arrays.asList; import static org.junit.Assert.assertThat; import static testsupport.matchers.IterableMatcher.iterates; diff --git a/src/test/java/com/jnape/palatable/lambda/functions/specialized/PredicateTest.java b/src/test/java/com/jnape/palatable/lambda/functions/specialized/PredicateTest.java new file mode 100644 index 000000000..b7c8be809 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/functions/specialized/PredicateTest.java @@ -0,0 +1,54 @@ +package com.jnape.palatable.lambda.functions.specialized; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class PredicateTest { + + @Test + public void jufPredicateTest() { + Predicate notEmpty = s -> !(s == null || s.length() == 0); + + assertTrue(notEmpty.test("foo")); + assertFalse(notEmpty.test("")); + assertFalse(notEmpty.test(null)); + } + + @Test + public void jufPredicateAnd() { + Predicate notEmpty = s -> !(s == null || s.length() == 0); + Predicate lengthGt1 = s -> s.length() > 1; + + Predicate conjunction = notEmpty.and(lengthGt1); + + assertTrue(conjunction.test("fo")); + assertFalse(conjunction.test("f")); + assertFalse(conjunction.test("")); + assertFalse(conjunction.test(null)); + } + + @Test + public void jufPredicateOr() { + Predicate notEmpty = s -> !(s == null || s.length() == 0); + Predicate lengthGt1 = s -> s != null && s.length() > 1; + + Predicate disjunction = lengthGt1.or(notEmpty); + + assertTrue(disjunction.test("fo")); + assertTrue(disjunction.test("f")); + assertFalse(disjunction.test("")); + assertFalse(disjunction.test(null)); + } + + @Test + public void jufPredicateNegate() { + Predicate isTrue = x -> x; + + assertTrue(isTrue.test(true)); + assertFalse(isTrue.test(false)); + assertFalse(isTrue.negate().test(true)); + assertTrue(isTrue.negate().test(false)); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/applicative/BiFunctorTest.java b/src/test/java/com/jnape/palatable/lambda/functor/BifunctorTest.java similarity index 60% rename from src/test/java/com/jnape/palatable/lambda/applicative/BiFunctorTest.java rename to src/test/java/com/jnape/palatable/lambda/functor/BifunctorTest.java index 9e23cd320..d5b779e6d 100644 --- a/src/test/java/com/jnape/palatable/lambda/applicative/BiFunctorTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functor/BifunctorTest.java @@ -1,8 +1,8 @@ -package com.jnape.palatable.lambda.applicative; +package com.jnape.palatable.lambda.functor; import com.jnape.palatable.lambda.functions.MonadicFunction; import org.junit.Test; -import testsupport.applicatives.InvocationRecordingBiFunctor; +import testsupport.applicatives.InvocationRecordingBifunctor; import java.util.concurrent.atomic.AtomicReference; @@ -10,21 +10,21 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; -public class BiFunctorTest { +public class BifunctorTest { @Test public void biMapLUsesIdentityForRightBiMapFunction() { AtomicReference rightInvocation = new AtomicReference<>(); - BiFunctor biFunctor = new InvocationRecordingBiFunctor<>(new AtomicReference<>(), rightInvocation); - biFunctor.biMapL(String::toUpperCase); + Bifunctor bifunctor = new InvocationRecordingBifunctor<>(new AtomicReference<>(), rightInvocation); + bifunctor.biMapL(String::toUpperCase); assertThat(rightInvocation.get(), is(id())); } @Test public void biMapRUsesIdentityForLeftBiMapFunction() { AtomicReference leftInvocation = new AtomicReference<>(); - BiFunctor biFunctor = new InvocationRecordingBiFunctor<>(leftInvocation, new AtomicReference<>()); - biFunctor.biMapR(String::valueOf); + Bifunctor bifunctor = new InvocationRecordingBifunctor<>(leftInvocation, new AtomicReference<>()); + bifunctor.biMapR(String::valueOf); assertThat(leftInvocation.get(), is(id())); } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/applicative/ProFunctorTest.java b/src/test/java/com/jnape/palatable/lambda/functor/ProfunctorTest.java similarity index 60% rename from src/test/java/com/jnape/palatable/lambda/applicative/ProFunctorTest.java rename to src/test/java/com/jnape/palatable/lambda/functor/ProfunctorTest.java index 948521446..d99fb1478 100644 --- a/src/test/java/com/jnape/palatable/lambda/applicative/ProFunctorTest.java +++ b/src/test/java/com/jnape/palatable/lambda/functor/ProfunctorTest.java @@ -1,8 +1,8 @@ -package com.jnape.palatable.lambda.applicative; +package com.jnape.palatable.lambda.functor; import com.jnape.palatable.lambda.functions.MonadicFunction; import org.junit.Test; -import testsupport.applicatives.InvocationRecordingProFunctor; +import testsupport.applicatives.InvocationRecordingProfunctor; import java.util.concurrent.atomic.AtomicReference; @@ -10,21 +10,21 @@ import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; -public class ProFunctorTest { +public class ProfunctorTest { @Test public void diMapLUsesIdentityForRightDiMapFunction() { AtomicReference rightInvocation = new AtomicReference<>(); - ProFunctor proFunctor = new InvocationRecordingProFunctor<>(new AtomicReference<>(), rightInvocation); - proFunctor.diMapL(Object::toString); + Profunctor profunctor = new InvocationRecordingProfunctor<>(new AtomicReference<>(), rightInvocation); + profunctor.diMapL(Object::toString); assertThat(rightInvocation.get(), is(id())); } @Test public void diMapRUsesIdentityForLeftDiMapFunction() { AtomicReference leftInvocation = new AtomicReference<>(); - ProFunctor proFunctor = new InvocationRecordingProFunctor<>(leftInvocation, new AtomicReference<>()); - proFunctor.diMapR(String::valueOf); + Profunctor profunctor = new InvocationRecordingProfunctor<>(leftInvocation, new AtomicReference<>()); + profunctor.diMapR(String::valueOf); assertThat(leftInvocation.get(), is(id())); } } \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/iterators/CombinatorialIteratorTest.java b/src/test/java/com/jnape/palatable/lambda/iterators/CombinatorialIteratorTest.java index 0b280a202..b7449b298 100644 --- a/src/test/java/com/jnape/palatable/lambda/iterators/CombinatorialIteratorTest.java +++ b/src/test/java/com/jnape/palatable/lambda/iterators/CombinatorialIteratorTest.java @@ -1,6 +1,6 @@ package com.jnape.palatable.lambda.iterators; -import com.jnape.palatable.lambda.tuples.Tuple2; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/src/test/java/com/jnape/palatable/lambda/iterators/ScanningIteratorTest.java b/src/test/java/com/jnape/palatable/lambda/iterators/ScanningIteratorTest.java new file mode 100644 index 000000000..ed93c0042 --- /dev/null +++ b/src/test/java/com/jnape/palatable/lambda/iterators/ScanningIteratorTest.java @@ -0,0 +1,43 @@ +package com.jnape.palatable.lambda.iterators; + +import org.junit.Test; + +import java.util.NoSuchElementException; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyIterator; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +public class ScanningIteratorTest { + + public static final com.jnape.palatable.lambda.functions.DyadicFunction ADD = (x, y) -> x + y; + + @Test + public void hasNextAtLeastForB() { + ScanningIterator scanningIterator = new ScanningIterator<>(ADD, 0, emptyIterator()); + assertThat(scanningIterator.hasNext(), is(true)); + } + + @Test + public void nextIsTrueAtLeastForB() { + ScanningIterator scanningIterator = new ScanningIterator<>(ADD, 0, emptyIterator()); + assertThat(scanningIterator.next(), is(0)); + } + + @Test + public void iteratesAsAfterB() { + ScanningIterator scanningIterator = new ScanningIterator<>(ADD, 0, asList(1, 2, 3, 4, 5).iterator()); + + scanningIterator.next(); + assertThat(scanningIterator.hasNext(), is(true)); + assertThat(scanningIterator.next(), is(ADD.apply(0, 1))); + } + + @Test(expected = NoSuchElementException.class) + public void failsOnNextIfOutOfAs() { + ScanningIterator scanningIterator = new ScanningIterator<>(ADD, 0, emptyIterator()); + scanningIterator.next(); + scanningIterator.next(); + } +} \ No newline at end of file diff --git a/src/test/java/com/jnape/palatable/lambda/iterators/UnfoldingIteratorTest.java b/src/test/java/com/jnape/palatable/lambda/iterators/UnfoldingIteratorTest.java index 097e4f938..0029e5e69 100644 --- a/src/test/java/com/jnape/palatable/lambda/iterators/UnfoldingIteratorTest.java +++ b/src/test/java/com/jnape/palatable/lambda/iterators/UnfoldingIteratorTest.java @@ -1,12 +1,12 @@ package com.jnape.palatable.lambda.iterators; +import com.jnape.palatable.lambda.adt.tuples.Tuple2; import com.jnape.palatable.lambda.functions.MonadicFunction; -import com.jnape.palatable.lambda.tuples.Tuple2; import org.junit.Test; import java.util.Optional; -import static com.jnape.palatable.lambda.tuples.Tuple2.tuple; +import static com.jnape.palatable.lambda.adt.tuples.Tuple2.tuple; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; diff --git a/src/test/java/com/jnape/palatable/lambda/tuples/Tuple3Test.java b/src/test/java/com/jnape/palatable/lambda/tuples/Tuple3Test.java deleted file mode 100644 index 72625e898..000000000 --- a/src/test/java/com/jnape/palatable/lambda/tuples/Tuple3Test.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.jnape.palatable.lambda.tuples; - -import org.junit.Test; - -import static com.jnape.palatable.lambda.tuples.Tuple3.tuple; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; - -public class Tuple3Test { - - @Test - public void hasThreeSlots() { - Tuple3 integerStringCharacterTuple = tuple(1, "two", '3'); - - assertThat(integerStringCharacterTuple._1, is(1)); - assertThat(integerStringCharacterTuple._2, is("two")); - assertThat(integerStringCharacterTuple._3, is('3')); - } - - @Test - public void toStringIsReasonable() { - assertThat(tuple("foo", "bar", "baz").toString(), is("(foo, bar, baz)")); - } -} diff --git a/src/test/java/testsupport/applicatives/InvocationRecordingBiFunctor.java b/src/test/java/testsupport/applicatives/InvocationRecordingBifunctor.java similarity index 67% rename from src/test/java/testsupport/applicatives/InvocationRecordingBiFunctor.java rename to src/test/java/testsupport/applicatives/InvocationRecordingBifunctor.java index e0e09090b..86fa80278 100644 --- a/src/test/java/testsupport/applicatives/InvocationRecordingBiFunctor.java +++ b/src/test/java/testsupport/applicatives/InvocationRecordingBifunctor.java @@ -1,15 +1,15 @@ package testsupport.applicatives; -import com.jnape.palatable.lambda.applicative.BiFunctor; import com.jnape.palatable.lambda.functions.MonadicFunction; +import com.jnape.palatable.lambda.functor.Bifunctor; import java.util.concurrent.atomic.AtomicReference; -public final class InvocationRecordingBiFunctor implements BiFunctor { +public final class InvocationRecordingBifunctor implements Bifunctor { private final AtomicReference leftFn; private final AtomicReference rightFn; - public InvocationRecordingBiFunctor(AtomicReference leftFn, + public InvocationRecordingBifunctor(AtomicReference leftFn, AtomicReference rightFn) { this.leftFn = leftFn; this.rightFn = rightFn; @@ -17,10 +17,10 @@ public InvocationRecordingBiFunctor(AtomicReference leftFn, @Override @SuppressWarnings("unchecked") - public BiFunctor biMap(MonadicFunction f1, + public Bifunctor biMap(MonadicFunction f1, MonadicFunction f2) { leftFn.set(f1); rightFn.set(f2); - return (BiFunctor) this; + return (Bifunctor) this; } } diff --git a/src/test/java/testsupport/applicatives/InvocationRecordingProFunctor.java b/src/test/java/testsupport/applicatives/InvocationRecordingProfunctor.java similarity index 67% rename from src/test/java/testsupport/applicatives/InvocationRecordingProFunctor.java rename to src/test/java/testsupport/applicatives/InvocationRecordingProfunctor.java index deef7c403..7cb8bef0d 100644 --- a/src/test/java/testsupport/applicatives/InvocationRecordingProFunctor.java +++ b/src/test/java/testsupport/applicatives/InvocationRecordingProfunctor.java @@ -1,15 +1,15 @@ package testsupport.applicatives; -import com.jnape.palatable.lambda.applicative.ProFunctor; import com.jnape.palatable.lambda.functions.MonadicFunction; +import com.jnape.palatable.lambda.functor.Profunctor; import java.util.concurrent.atomic.AtomicReference; -public final class InvocationRecordingProFunctor implements ProFunctor { +public final class InvocationRecordingProfunctor implements Profunctor { private final AtomicReference leftFn; private final AtomicReference rightFn; - public InvocationRecordingProFunctor(AtomicReference leftFn, + public InvocationRecordingProfunctor(AtomicReference leftFn, AtomicReference rightFn) { this.leftFn = leftFn; this.rightFn = rightFn; @@ -17,10 +17,10 @@ public InvocationRecordingProFunctor(AtomicReference leftFn, @Override @SuppressWarnings("unchecked") - public ProFunctor diMap(MonadicFunction f1, + public Profunctor diMap(MonadicFunction f1, MonadicFunction f2) { leftFn.set(f1); rightFn.set(f2); - return (ProFunctor) this; + return (Profunctor) this; } }