From 21b23158eecbf90a50eb29c6ca6d35a36e256638 Mon Sep 17 00:00:00 2001 From: Drew Taylor Date: Wed, 28 Oct 2020 15:34:00 -0400 Subject: [PATCH] Fixes #426. Add fj.data.NonEmptyList.sequence*, traverse*, foldLeftN, foldRightN, init, last. --- core/src/main/java/fj/data/NonEmptyList.java | 624 ++++++++++++++++-- .../test/java/fj/data/NonEmptyListTest.java | 243 +++++++ 2 files changed, 810 insertions(+), 57 deletions(-) create mode 100644 core/src/test/java/fj/data/NonEmptyListTest.java diff --git a/core/src/main/java/fj/data/NonEmptyList.java b/core/src/main/java/fj/data/NonEmptyList.java index 4073476d..6f0ced7b 100644 --- a/core/src/main/java/fj/data/NonEmptyList.java +++ b/core/src/main/java/fj/data/NonEmptyList.java @@ -1,14 +1,12 @@ package fj.data; import fj.*; -import fj.function.Effect1; +import fj.control.Trampoline; import java.util.Collection; import java.util.Iterator; -import static fj.Function.flip; -import static fj.Function.identity; -import static fj.Function.uncurryF2; +import static fj.Function.*; import static fj.data.Option.some; import static fj.data.Option.somes; @@ -35,12 +33,26 @@ public Iterator iterator() { /** * The first element of this linked list. */ - public A head() { return head; } + public A head() { + return head; + } /** * This list without the first element. */ - public List tail() { return tail; } + public List tail() { + return tail; + } + + public List init() + { + return toList().init(); + } + + public A last() + { + return toList().last(); + } private NonEmptyList(final A head, final List tail) { this.head = head; @@ -72,7 +84,9 @@ public NonEmptyList snoc(final A a) { * * @return The length of this list. */ - public int length() { return 1 + tail.length(); } + public int length() { + return 1 + tail.length(); + } /** * Appends the given list to this list. @@ -99,40 +113,6 @@ public NonEmptyList append(final NonEmptyList as) { return nel(head, bb); } - /** - * Performs a right-fold reduction across this list. This function uses O(length) stack space. - */ - public final A foldRight1(final F> f) { - return reverse().foldLeft1(flip(f)); - } - - /** - * Performs a right-fold reduction across this list. This function uses O(length) stack space. - */ - public final A foldRight1(final F2 f) { - return reverse().foldLeft1(flip(f)); - } - - /** - * Performs a left-fold reduction across this list. This function runs in constant space. - */ - public final A foldLeft1(final F> f) { - return foldLeft1(uncurryF2(f)); - } - - /** - * Performs a left-fold reduction across this list. This function runs in constant space. - */ - public final A foldLeft1(final F2 f) { - A x = head; - - for (List xs = tail; !xs.isEmpty(); xs = xs.tail()) { - x = f.f(x, xs.head()); - } - - return x; - } - /** * Maps the given function across this list. * @@ -155,9 +135,9 @@ public NonEmptyList bind(final F> f) { b.snoc(p.head); b.append(p.tail); tail.foreachDoEffect(a -> { - final NonEmptyList p1 = f.f(a); - b.snoc(p1.head); - b.append(p1.tail); + final NonEmptyList p1 = f.f(a); + b.snoc(p1.head); + b.append(p1.tail); }); final List bb = b.toList(); return nel(bb.head(), bb.tail()); @@ -170,8 +150,8 @@ public NonEmptyList bind(final F> f) { */ public NonEmptyList> sublists() { return fromList( - somes(toList().toStream().substreams() - .map(F1Functions.o(NonEmptyList::fromList, Conversions.Stream_List())).toList())).some(); + somes(toList().toStream().substreams() + .map(F1Functions.o(NonEmptyList::fromList, Conversions.Stream_List())).toList())).some(); } /** @@ -226,7 +206,7 @@ public NonEmptyList sort(final Ord o) { return nel(list.head(), list.tail()); } - + /** * Returns the minimum element in this non empty list according to the given ordering. * @@ -236,7 +216,7 @@ public NonEmptyList sort(final Ord o) { public final A minimum(final Ord o) { return foldLeft1(o::min); } - + /** * Returns the maximum element in this non empty list according to the given ordering. * @@ -246,8 +226,8 @@ public final A minimum(final Ord o) { public final A maximum(final Ord o) { return foldLeft1(o::max); } - - + + /** * Zips this non empty list with the given non empty list to produce a list of pairs. If this list and the given list * have different lengths, then the longer list is normalised so this function never fails. @@ -355,7 +335,8 @@ public static NonEmptyList nel(final A head, final List tail) { * @param tail The elements to construct a list's tail with. * @return A non-empty list with the given elements. */ - @SafeVarargs public static NonEmptyList nel(final A head, final A... tail) { + @SafeVarargs + public static NonEmptyList nel(final A head, final A... tail) { return nel(head, List.list(tail)); } @@ -376,8 +357,8 @@ public static F> nel() { */ public static Option> fromList(final List as) { return as.isEmpty() ? - Option.none() : - some(nel(as.head(), as.tail())); + Option.none() : + some(nel(as.head(), as.tail())); } /** @@ -386,7 +367,9 @@ public static Option> fromList(final List as) { * @param o The non empty list of non empty lists to join. * @return A new non empty list that is the concatenation of the given lists. */ - public static NonEmptyList join(final NonEmptyList> o) { return o.bind(identity()); } + public static NonEmptyList join(final NonEmptyList> o) { + return o.bind(identity()); + } /** * Perform an equality test on this list which delegates to the .equals() method of the member instances. @@ -395,13 +378,540 @@ public static Option> fromList(final List as) { * @param obj the other object to check for equality against. * @return true if this list is equal to the provided argument */ - @Override public boolean equals( final Object obj ) { + @Override + public boolean equals(final Object obj) { return Equal.equals0(NonEmptyList.class, this, obj, () -> Equal.nonEmptyListEqual(Equal.anyEqual())); } - @Override public int hashCode() { + @Override + public int hashCode() { return Hash.nonEmptyListHash(Hash.anyHash()).hash(this); } - @Override public String toString() { return Show.nonEmptyListShow(Show.anyShow()).showS(this); } + @Override + public String toString() { + return Show.nonEmptyListShow(Show.anyShow()).showS(this); + } + + /** + * Fold the list, from right to left. + * + * @param combine the combine function + * @return the output + */ + public final A foldRight1( + final F> combine) { + return foldRightN(combine, identity()); + } + + /** + * Fold the list, from right to left. + * + * @param combine the combine function + * @return the output + */ + public final A foldRight1( + final F2 combine) { + return foldRightN(combine, identity()); + } + + /** + * Fold the list, from left to right. + * + * @param combine the combine function + * @return the output + */ + public final A foldLeft1( + final F> combine) { + return foldLeftN(combine, identity()); + } + + /** + * Fold the list, from left to right. + * + * @param combine the combine function + * @return the output + */ + public final A foldLeft1( + final F2 combine) { + return foldLeftN(combine, identity()); + } + + /** + * Fold the list, from right to left, using one function to transform the last and another function to combine the + * output. + * + * @param combine the combine function + * @param transform the transform function + * @param the type of the output + * @return the output + */ + public final B foldRightN( + final F> combine, + final F transform) { + return tail().isEmpty() ? + transform.f(head()) : + init().foldRight(combine, transform.f(last())); + } + + /** + * Fold the list, from right to left, using one function to transform the last and another function to combine the + * output. + * + * @param combine the combine function + * @param transform the transform function + * @param the type of the output + * @return the output + */ + public final B foldRightN( + final F2 combine, + final F transform) { + return tail().isEmpty() ? + transform.f(head()) : + init().foldRight(combine, transform.f(last())); + } + + /** + * Fold the list, from left to right, using one function to transform the head and another function to combine the + * output. + * + * @param combine the combine function + * @param transform the transform function + * @param the type of the output + * @return the output + */ + public final B foldLeftN( + final F> combine, + final F transform) { + return tail().isEmpty() ? + transform.f(head()) : + tail().foldLeft(combine, transform.f(head())); + } + + /** + * Fold the list, from left to right, using one function to transform the head and another function to combine the + * output. + * + * @param combine the combine function + * @param transform the transform function + * @param the type of the output + * @return the output + */ + public final B foldLeftN( + final F2 combine, + final F transform) { + return tail().isEmpty() ? + transform.f(head()) : + tail().foldLeft(combine, transform.f(head())); + } + + /** + * Sequence the given nonEmptyList and collect the output on the right side of an either. + * + * @param nonEmptyList the given nonEmptyList + * @param the type of the right value + * @param the type of the left value + * @return the either + */ + public static Either> sequenceEither( + final NonEmptyList> nonEmptyList) { + return nonEmptyList.traverseEither(identity()); + } + + /** + * Sequence the given nonEmptyList and collect the output on the left side of an either. + * + * @param nonEmptyList the given nonEmptyList + * @param the type of the right value + * @param the type of the left value + * @return the either + */ + public static Either, R> sequenceEitherLeft( + final NonEmptyList> nonEmptyList) { + return nonEmptyList.traverseEitherLeft(identity()); + } + + /** + * Sequence the given nonEmptyList and collect the output on the right side of an either. + * + * @param nonEmptyList the given nonEmptyList + * @param the type of the right value + * @param the type of the left value + * @return the either + */ + public static Either> sequenceEitherRight( + final NonEmptyList> nonEmptyList) { + return nonEmptyList.traverseEitherRight(identity()); + } + + /** + * Sequence the given nonEmptyList and collect the output as a function. + * + * @param nonEmptyList the given nonEmptyList + * @param the type of the input value + * @param the type of the output value + * @return the either + */ + public static F> sequenceF( + final NonEmptyList> nonEmptyList) { + return nonEmptyList.traverseF(identity()); + } + + /** + * Sequence the given nonEmptyList and collect the output as an IO. + * + * @param nonEmptyList the given nonEmptyList + * @param the type of the IO value + * @return the IO + */ + public static IO> sequenceIO( + final NonEmptyList> nonEmptyList) { + return nonEmptyList.traverseIO(identity()); + } + + /** + * Sequence the given nonEmptyList and collect the output as a list. + * + * @param nonEmptyList the given nonEmptyList + * @param the type of the list value + * @return the list + */ + public static List> sequenceList( + final NonEmptyList> nonEmptyList) { + return nonEmptyList.traverseList(identity()); + } + + /** + * Sequence the given nonEmptyList and collect the output as a nonEmptyList. + * + * @param nonEmptyList the given nonEmptyList + * @param the type of the nonEmptyList value + * @return the nonEmptyList + */ + public static NonEmptyList> sequenceNonEmptyList( + final NonEmptyList> nonEmptyList) { + return nonEmptyList.traverseNonEmptyList(identity()); + } + + /** + * Sequence the given nonEmptyList and collect the output as an nonEmptyList. + * + * @param nonEmptyList the given nonEmptyList + * @param the type of the option value + * @return the nonEmptyList + */ + public static Option> sequenceOption( + final NonEmptyList> nonEmptyList) { + return nonEmptyList.traverseOption(identity()); + } + + /** + * Sequence the given nonEmptyList and collect the output as a P1. + * + * @param nonEmptyList the given nonEmptyList + * @param the type of the P1 value + * @return the P1 + */ + public static P1> sequenceP1( + final NonEmptyList> nonEmptyList) { + return nonEmptyList.traverseP1(identity()); + } + + /** + * Sequence the given nonEmptyList and collect the output as a seq. + * + * @param nonEmptyList the given nonEmptyList + * @param the type of the nonEmptyList value + * @return the seq + */ + public static Seq> sequenceSeq( + final NonEmptyList> nonEmptyList) { + return nonEmptyList.traverseSeq(identity()); + } + + /** + * Sequence the given nonEmptyList and collect the output as a set; use the given ord to order the set. + * + * @param ord the given ord + * @param nonEmptyList the given nonEmptyList + * @param the type of the set value + * @return the either + */ + public static Set> sequenceSet( + final Ord ord, + final NonEmptyList> nonEmptyList) { + return nonEmptyList.traverseSet(ord, identity()); + } + + /** + * Sequence the given nonEmptyList and collect the output as a stream. + * + * @param nonEmptyList the given nonEmptyList + * @param the type of the nonEmptyList value + * @return the stream + */ + public static Stream> sequenceStream( + final NonEmptyList> nonEmptyList) { + return nonEmptyList.traverseStream(identity()); + } + + /** + * Sequence the given nonEmptyList and collect the output as a trampoline. + * + * @param nonEmptyList the given trampoline + * @param the type of the nonEmptyList value + * @return the nonEmptyList + */ + public static Trampoline> sequenceTrampoline( + final NonEmptyList> nonEmptyList) { + return nonEmptyList.traverseTrampoline(identity()); + } + + /** + * Sequence the given nonEmptyList and collect the output as a validation. + * + * @param nonEmptyList the given nonEmptyList + * @param the type of the failure value + * @param the type of the success value + * @return the validation + */ + public static Validation> sequenceValidation( + final NonEmptyList> nonEmptyList) { + return nonEmptyList.traverseValidation(identity()); + } + + /** + * Sequence the given nonEmptyList and collect the output as a validation; use the given semigroup to reduce the errors. + * + * @param semigroup the given semigroup + * @param nonEmptyList the given nonEmptyList + * @param the type of the failure value + * @param the type of the success value + * @return the validation + */ + public static Validation> sequenceValidation( + final Semigroup semigroup, + final NonEmptyList> nonEmptyList) { + return nonEmptyList.traverseValidation(semigroup, identity()); + } + + /** + * Traverse this nonEmptyList with the given function and collect the output on the right side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the either + */ + public Either> traverseEither( + final F> f) { + return traverseEitherRight(f); + } + + /** + * Traverse this nonEmptyList with the given function and collect the output on the left side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the either + */ + public Either, R> traverseEitherLeft( + final F> f) { + return foldRightN( + element -> either -> f.f(element).left().bind(elementInner -> either.left().map(nonEmptyList -> nonEmptyList.cons(elementInner))), + element -> f.f(element).left().map(elementInner -> nel(elementInner))); + } + + /** + * Traverse this nonEmptyList with the given function and collect the output on the right side of an either. + * + * @param f the given function + * @param the type of the left value + * @param the type of the right value + * @return the either + */ + public Either> traverseEitherRight( + final F> f) { + return foldRightN( + element -> either -> f.f(element).right().bind(elementInner -> either.right().map(nonEmptyList -> nonEmptyList.cons(elementInner))), + element -> f.f(element).right().map(elementInner -> nel(elementInner))); + } + + /** + * Traverse this nonEmptyList with the given function and collect the output as a function. + * + * @param f the given function + * @param the type of the input value + * @param the type of the output value + * @return the function + */ + public F> traverseF( + final F> f) { + return foldRightN( + element -> fInner -> Function.bind(f.f(element), elementInner -> andThen(fInner, nonEmptyList -> nonEmptyList.cons(elementInner))), + element -> F1Functions.map(f.f(element), elementInner -> nel(elementInner))); + } + + /** + * Traverse this nonEmptyList with the given function and collect the output as an IO. + * + * @param f the given function + * @param the type of the IO value + * @return the IO + */ + public IO> traverseIO( + final F> f) { + return foldRightN( + element -> io -> IOFunctions.bind(f.f(element), elementInner -> IOFunctions.map(io, nonEmptyList -> nonEmptyList.cons(elementInner))), + element -> IOFunctions.map(f.f(element), elementInner -> nel(elementInner))); + } + + /** + * Traverse this nonEmptyList with the given function and collect the output as a list. + * + * @param f the given function + * @param the type of the list value + * @return the list + */ + public List> traverseList( + final F> f) { + return foldRightN( + element -> list -> f.f(element).bind(elementInner -> list.map(nonEmptyList -> nonEmptyList.cons(elementInner))), + element -> f.f(element).map(elementInner -> nel(elementInner))); + } + + /** + * Traverse this nonEmptyList with the given function and collect the output as a nonEmptyList. + * + * @param f the given function + * @param the type of the nonEmptyList value + * @return the nonEmptyList + */ + public NonEmptyList> traverseNonEmptyList( + final F> f) { + return foldRightN( + element -> nonEmptyList -> f.f(element).bind(elementInner -> nonEmptyList.map(nonEmptyListInner -> nonEmptyListInner.cons(elementInner))), + element -> f.f(element).map(elementInner -> nel(elementInner))); + } + + /** + * Traverses through the Seq with the given function + * + * @param f The function that produces Option value + * @return none if applying f returns none to any element of the seq or f mapped seq in some . + */ + public Option> traverseOption( + final F> f) { + return foldRightN( + element -> option -> f.f(element).bind(elementInner -> option.map(nonEmptyList -> nonEmptyList.cons(elementInner))), + element -> f.f(element).map(elementInner -> nel(elementInner))); + } + + /** + * Traverse this nonEmptyList with the given function and collect the output as a p1. + * + * @param f the given function + * @param the type of the p1 value + * @return the p1 + */ + public P1> traverseP1( + final F> f) { + return foldRightN( + element -> p1 -> f.f(element).bind(elementInner -> p1.map(nonEmptyList -> nonEmptyList.cons(elementInner))), + element -> f.f(element).map(elementInner -> nel(elementInner))); + } + + /** + * Traverse this nonEmptyList with the given function and collect the output as a seq. + * + * @param f the given function + * @param the type of the seq value + * @return the seq + */ + public Seq> traverseSeq( + final F> f) { + return foldRightN( + element -> seq -> f.f(element).bind(elementInner -> seq.map(nonEmptyList -> nonEmptyList.cons(elementInner))), + element -> f.f(element).map(elementInner -> nel(elementInner))); + } + + /** + * Traverse this nonEmptyList with the given function and collect the output as a set; use the given ord to order the + * set. + * + * @param ord the given ord + * @param f the given function + * @param the type of the set value + * @return the set + */ + public Set> traverseSet( + final Ord ord, + final F> f) { + final Ord> seqOrd = Ord.nonEmptyListOrd(ord); + return foldRightN( + element -> set -> f.f(element).bind(seqOrd, elementInner -> set.map(seqOrd, seq -> seq.cons(elementInner))), + element -> f.f(element).map(seqOrd, elementInner -> nel(elementInner))); + } + + /** + * Traverse this nonEmptyList with the given function and collect the output as a stream. + * + * @param f the given function + * @param the type of the nonEmptyList value + * @return the stream + */ + public Stream> traverseStream( + final F> f) { + return foldRightN( + element -> stream -> f.f(element).bind(elementInner -> stream.map(nonEmptyList -> nonEmptyList.cons(elementInner))), + element -> f.f(element).map(elementInner -> nel(elementInner))); + } + + /** + * Traverse this nonEmptyList with the given function and collect the output as a trampoline. + * + * @param f the given function + * @param the type of the trampoline value + * @return the trampoline + */ + public Trampoline> traverseTrampoline( + final F> f) { + return foldRightN( + element -> trampoline -> f.f(element).bind(elementInner -> trampoline.map(seq -> seq.cons(elementInner))), + element -> f.f(element).map(elementInner -> nel(elementInner))); + } + + /** + * Traverse this nonEmptyList with the given function and collect the output as a validation. + * + * @param f the given function + * @param the type of the failure value + * @param the type of the success value + * @return the validation + */ + public final Validation> traverseValidation( + final F> f) { + return foldRightN( + element -> validation -> f.f(element).bind(elementInner -> validation.map(nonEmptyList -> nonEmptyList.cons(elementInner))), + element -> f.f(element).map(elementInner -> nel(elementInner))); + } + + /** + * Traverse this nonEmptyList with the given function and collect the output as a validation; use the given semigroup to + * reduce the errors. + * + * @param semigroup the given semigroup + * @param f the given function + * @param the type of the failure value + * @param the type of the success value + * @return the validation + */ + public final Validation> traverseValidation( + final Semigroup semigroup, + final F> f) { + return foldRightN( + element -> validation -> f.f(element).map(NonEmptyList::nel).accumulate(semigroup, validation, (nel1, nel2) -> nel1.append(nel2)), + element -> f.f(element).map(elementInner -> nel(elementInner))); + } } diff --git a/core/src/test/java/fj/data/NonEmptyListTest.java b/core/src/test/java/fj/data/NonEmptyListTest.java new file mode 100644 index 00000000..99630193 --- /dev/null +++ b/core/src/test/java/fj/data/NonEmptyListTest.java @@ -0,0 +1,243 @@ +package fj.data; + +import fj.control.Trampoline; +import org.junit.Test; + +import java.io.IOException; + +import static fj.Function.constant; +import static fj.Ord.*; +import static fj.P.p; +import static fj.Semigroup.nonEmptyListSemigroup; +import static fj.data.Either.left; +import static fj.data.Either.right; +import static fj.data.List.arrayList; +import static fj.data.NonEmptyList.*; +import static fj.data.Option.none; +import static fj.data.Option.some; +import static fj.data.Stream.nil; +import static org.junit.Assert.assertEquals; + +public class NonEmptyListTest { + @Test + public void testSequenceEither() { + assertEquals(right(nel("zero")), sequenceEither(nel(right("zero")))); + assertEquals(left("zero"), sequenceEither(nel(left("zero")))); + + assertEquals(right(nel("zero", "one")), sequenceEither(nel(right("zero"), right("one")))); + assertEquals(left("one"), sequenceEither(nel(right("zero"), left("one")))); + assertEquals(left("zero"), sequenceEither(nel(left("zero"), right("one")))); + assertEquals(left("zero"), sequenceEither(nel(left("zero"), left("one")))); + } + + @Test + public void testSequenceEitherLeft() { + assertEquals(right("zero"), sequenceEitherLeft(nel(right("zero")))); + assertEquals(left(nel("zero")), sequenceEitherLeft(nel(left("zero")))); + + assertEquals(right("zero"), sequenceEitherLeft(nel(right("zero"), right("one")))); + assertEquals(right("zero"), sequenceEitherLeft(nel(right("zero"), left("one")))); + assertEquals(right("one"), sequenceEitherLeft(nel(left("zero"), right("one")))); + assertEquals(left(nel("zero", "one")), sequenceEitherLeft(nel(left("zero"), left("one")))); + } + + @Test + public void testSequenceEitherRight() { + assertEquals(right(nel("zero")), sequenceEitherRight(nel(right("zero")))); + assertEquals(left("zero"), sequenceEitherRight(nel(left("zero")))); + + assertEquals(right(nel("zero", "one")), sequenceEitherRight(nel(right("zero"), right("one")))); + assertEquals(left("one"), sequenceEitherRight(nel(right("zero"), left("one")))); + assertEquals(left("zero"), sequenceEitherRight(nel(left("zero"), right("one")))); + assertEquals(left("zero"), sequenceEitherRight(nel(left("zero"), left("one")))); + } + + @Test + public void testSequenceF() { + assertEquals(constant(nel("zero")).f(1), sequenceF(nel(constant("zero"))).f(1)); + assertEquals(constant(nel("zero", "one")).f(1), sequenceF(nel(constant("zero"), constant("one"))).f(1)); + } + + @Test + public void testSequenceIO() + throws IOException { + assertEquals(IOFunctions.lazy(constant(nel("zero"))).run(), sequenceIO(nel(IOFunctions.lazy(constant("zero")))).run()); + } + + @Test + public void testSequenceList() { + assertEquals(arrayList(nel("zero")), sequenceList(nel(arrayList("zero")))); + assertEquals(arrayList(nel("zero"), nel("one")), sequenceList(nel(arrayList("zero", "one")))); + } + + @Test + public void testSequenceOption() { + assertEquals(none(), sequenceOption(nel(none()))); + assertEquals(some(nel("zero")), sequenceOption(nel(some("zero")))); + } + + @Test + public void testSequenceP1() { + assertEquals(p(nel("zero")), sequenceP1(nel(p("zero")))); + } + + @Test + public void testSequenceSeq() { + assertEquals(Seq.empty(), sequenceSeq(nel(Seq.empty()))); + assertEquals(Seq.single(nel("zero")), sequenceSeq(nel(Seq.single("zero")))); + assertEquals(Seq.arraySeq(nel("zero"), nel("one")), sequenceSeq(nel(Seq.arraySeq("zero", "one")))); + } + + @Test + public void testSequenceSet() { + assertEquals(Set.empty(nonEmptyListOrd(stringOrd)), sequenceSet(stringOrd, nel(Set.empty(stringOrd)))); + assertEquals(Set.arraySet(nonEmptyListOrd(stringOrd), nel("zero")), sequenceSet(stringOrd, nel(Set.single(stringOrd, "zero")))); + assertEquals(Set.arraySet(nonEmptyListOrd(stringOrd), nel("zero"), nel("one")), sequenceSet(stringOrd, nel(Set.arraySet(stringOrd, "zero", "one")))); + } + + @Test + public void testSequenceStream() { + assertEquals(nil(), sequenceStream(nel(nil()))); + assertEquals(Stream.single(nel("zero")), sequenceStream(nel(Stream.single("zero")))); + assertEquals(Stream.arrayStream(nel("zero"), nel("one")), sequenceStream(nel(Stream.arrayStream("zero", "one")))); + } + + @Test + public void testSequenceTrampoline() { + assertEquals(Trampoline.pure(nel(0)).run(), sequenceTrampoline(nel(Trampoline.pure(0))).run()); + assertEquals(Trampoline.pure(nel(0, 1)).run(), sequenceTrampoline(nel(Trampoline.pure(0), Trampoline.pure(1))).run()); + } + + @Test + public void testSequenceValidation() { + assertEquals(Validation.success(nel(0)), sequenceValidation(nel(Validation.success(0)))); + assertEquals(Validation.fail(nel(1)), sequenceValidation(nel(Validation.fail(nel(1))))); + + assertEquals(Validation.success(nel(0, 1)), sequenceValidation(nel(Validation.success(0), Validation.success(1)))); + assertEquals(Validation.fail(nel(1)), sequenceValidation(nel(Validation.success(0), Validation.fail(nel(1))))); + assertEquals(Validation.fail(nel(0)), sequenceValidation(nel(Validation.fail(nel(0)), Validation.success(1)))); + assertEquals(Validation.fail(nel(0)), sequenceValidation(nel(Validation.fail(nel(0)), Validation.fail(nel(1))))); + } + + @Test + public void testSequenceValidationSemigroup() { + assertEquals(Validation.success(nel(0)), sequenceValidation(nonEmptyListSemigroup(), nel(Validation.success(0)))); + assertEquals(Validation.fail(nel(1)), sequenceValidation(nonEmptyListSemigroup(), nel(Validation.fail(nel(1))))); + + assertEquals(Validation.success(nel(0, 1)), sequenceValidation(nonEmptyListSemigroup(), nel(Validation.success(0), Validation.success(1)))); + assertEquals(Validation.fail(nel(1)), sequenceValidation(nonEmptyListSemigroup(), nel(Validation.success(0), Validation.fail(nel(1))))); + assertEquals(Validation.fail(nel(0)), sequenceValidation(nonEmptyListSemigroup(), nel(Validation.fail(nel(0)), Validation.success(1)))); + assertEquals(Validation.fail(nel(0, 1)), sequenceValidation(nonEmptyListSemigroup(), nel(Validation.fail(nel(0)), Validation.fail(nel(1))))); + } + + @Test + public void testTraverseEither() { + assertEquals(right(nel(4)), nel(Either.right("zero")).traverseEither(either -> either.bimap(o -> o.length(), o -> o.length()))); + assertEquals(left(4), nel(Either.left("zero")).traverseEither(either -> either.bimap(o -> o.length(), o -> o.length()))); + + assertEquals(right(nel(4, 3)), nel(Either.right("zero"), Either.right("one")).traverseEither(either -> either.bimap(o -> o.length(), o -> o.length()))); + assertEquals(left(3), nel(Either.right("zero"), Either.left("one")).traverseEither(either -> either.bimap(o -> o.length(), o -> o.length()))); + assertEquals(left(4), nel(Either.left("zero"), Either.right("one")).traverseEither(either -> either.bimap(o -> o.length(), o -> o.length()))); + assertEquals(left(4), nel(Either.left("zero"), Either.left("one")).traverseEither(either -> either.bimap(o -> o.length(), o -> o.length()))); + } + + @Test + public void testTraverseEitherLeft() { + assertEquals(right(4), nel(Either.right("zero")).traverseEitherLeft(either -> either.bimap(o -> o.length(), o -> o.length()))); + assertEquals(left(nel(4)), nel(Either.left("zero")).traverseEitherLeft(either -> either.bimap(o -> o.length(), o -> o.length()))); + + assertEquals(right(4), nel(Either.right("zero"), Either.right("one")).traverseEitherLeft(either -> either.bimap(o -> o.length(), o -> o.length()))); + assertEquals(right(4), nel(Either.right("zero"), Either.left("one")).traverseEitherLeft(either -> either.bimap(o -> o.length(), o -> o.length()))); + assertEquals(right(3), nel(Either.left("zero"), Either.right("one")).traverseEitherLeft(either -> either.bimap(o -> o.length(), o -> o.length()))); + assertEquals(left(nel(4, 3)), nel(Either.left("zero"), Either.left("one")).traverseEitherLeft(either -> either.bimap(o -> o.length(), o -> o.length()))); + } + + @Test + public void testTraverseEitherRight() { + assertEquals(right(nel(4)), nel(Either.right("zero")).traverseEitherRight(either -> either.bimap(o -> o.length(), o -> o.length()))); + assertEquals(left(4), nel(Either.left("zero")).traverseEitherRight(either -> either.bimap(o -> o.length(), o -> o.length()))); + + assertEquals(right(nel(4, 3)), nel(Either.right("zero"), Either.right("one")).traverseEitherRight(either -> either.bimap(o -> o.length(), o -> o.length()))); + assertEquals(left(3), nel(Either.right("zero"), Either.left("one")).traverseEitherRight(either -> either.bimap(o -> o.length(), o -> o.length()))); + assertEquals(left(4), nel(Either.left("zero"), Either.right("one")).traverseEitherRight(either -> either.bimap(o -> o.length(), o -> o.length()))); + assertEquals(left(4), nel(Either.left("zero"), Either.left("one")).traverseEitherRight(either -> either.bimap(o -> o.length(), o -> o.length()))); + } + + @Test + public void testTraverseF() { + assertEquals(constant(nel("two")).f(1), nel(constant("zero")).traverseF(constant(constant("two"))).f(1)); + assertEquals(constant(nel("two", "two")).f(1), nel(constant("zero"), constant("one")).traverseF(constant(constant("two"))).f(1)); + } + + @Test + public void testTraverseIO() + throws IOException { + assertEquals(IOFunctions.lazy(constant(nel("one"))).run(), nel(IOFunctions.lazy(constant("zero"))).traverseIO(constant(IOFunctions.lazy(constant("one")))).run()); + } + + @Test + public void testTraverseList() { + assertEquals(arrayList(nel("two")), nel(arrayList("zero")).traverseList(list -> list.map(constant("two")))); + assertEquals(arrayList(nel("two"), nel("two")), nel(arrayList("zero", "one")).traverseList(list -> list.map(constant("two")))); + } + + @Test + public void testTraverseOption() { + assertEquals(none(), nel(Option.none()).traverseOption(option -> option.map(o -> o.length()))); + assertEquals(some(nel(4)), nel(some("zero")).traverseOption(option -> option.map(o -> o.length()))); + } + + @Test + public void testTraverseP1() { + assertEquals(p(nel(4)), nel(p("zero")).traverseP1(p -> p.map(o -> o.length()))); + } + + @Test + public void testTraverseSeq() { + assertEquals(Seq.empty(), nel(Seq.empty()).traverseSeq(seq -> seq.map(o -> o.length()))); + assertEquals(Seq.single(nel(4)), nel(Seq.single("zero")).traverseSeq(seq -> seq.map(o -> o.length()))); + assertEquals(Seq.arraySeq(nel(4), nel(3)), nel(Seq.arraySeq("zero", "one")).traverseSeq(seq -> seq.map(o -> o.length()))); + } + + @Test + public void testTraverseSet() { + assertEquals(Set.empty(nonEmptyListOrd(intOrd)), nel(Set.empty(stringOrd)).traverseSet(intOrd, set -> set.map(intOrd, o -> o.length()))); + assertEquals(Set.arraySet(nonEmptyListOrd(intOrd), nel(4)), nel(Set.single(stringOrd, "zero")).traverseSet(intOrd, set -> set.map(intOrd, o -> o.length()))); + assertEquals(Set.arraySet(nonEmptyListOrd(intOrd), nel(4), nel(3)), nel(Set.arraySet(stringOrd, "zero", "one")).traverseSet(intOrd, set -> set.map(intOrd, o -> o.length()))); + } + + @Test + public void testTraverseStream() { + assertEquals(Stream.single(nel(4)), nel(Stream.single("zero")).traverseStream(stream -> stream.map(o -> o.length()))); + assertEquals(Stream.arrayStream(nel(4), nel(3)), nel(Stream.arrayStream("zero", "one")).traverseStream(stream -> stream.map(o -> o.length()))); + } + + @Test + public void testTraverseTrampoline() { + assertEquals(Trampoline.pure(nel(1)).run(), nel(Trampoline.pure(0)).traverseTrampoline(trampoline -> trampoline.map(i -> i + 1)).run()); + assertEquals(Trampoline.pure(nel(1, 2)).run(), nel(Trampoline.pure(0), Trampoline.pure(1)).traverseTrampoline(trampoline -> trampoline.map(i -> i + 1)).run()); + } + + @Test + public void testTraverseValidation() { + assertEquals(Validation.success(nel(1)), nel(Validation., Integer>success(0)).traverseValidation(validation -> validation.map(i -> i + 1))); + assertEquals(Validation.fail(nel(1)), nel(Validation., Integer>fail(nel(1))).traverseValidation(validation -> validation.map(i -> i + 1))); + + assertEquals(Validation.success(nel(1, 2)), nel(Validation., Integer>success(0), Validation.success(1)).traverseValidation(validation -> validation.map(i -> i + 1))); + assertEquals(Validation.fail(nel(1)), nel(Validation., Integer>success(0), Validation., Integer>fail(nel(1))).traverseValidation(validation -> validation.map(i -> i + 1))); + assertEquals(Validation.fail(nel(0)), nel(Validation., Integer>fail(nel(0)), Validation., Integer>success(1)).traverseValidation(validation -> validation.map(i -> i + 1))); + assertEquals(Validation.fail(nel(0)), nel(Validation., Integer>fail(nel(0)), Validation., Integer>fail(nel(1))).traverseValidation(validation -> validation.map(i -> i + 1))); + } + + @Test + public void testTraverseValidationSemigroup() { + assertEquals(Validation.success(nel(1)), nel(Validation., Integer>success(0)).traverseValidation(nonEmptyListSemigroup(), validation -> validation.map(i -> i + 1))); + assertEquals(Validation.fail(nel(1)), nel(Validation., Integer>fail(nel(1))).traverseValidation(nonEmptyListSemigroup(), validation -> validation.map(i -> i + 1))); + + assertEquals(Validation.success(nel(1, 2)), nel(Validation., Integer>success(0), Validation., Integer>success(1)).traverseValidation(nonEmptyListSemigroup(), validation -> validation.map(i -> i + 1))); + assertEquals(Validation.fail(nel(1)), nel(Validation., Integer>success(0), Validation., Integer>fail(nel(1))).traverseValidation(nonEmptyListSemigroup(), validation -> validation.map(i -> i + 1))); + assertEquals(Validation.fail(nel(0)), nel(Validation., Integer>fail(nel(0)), Validation., Integer>success(1)).traverseValidation(nonEmptyListSemigroup(), validation -> validation.map(i -> i + 1))); + assertEquals(Validation.fail(nel(0, 1)), nel(Validation., Integer>fail(nel(0)), Validation., Integer>fail(nel(1))).traverseValidation(nonEmptyListSemigroup(), validation -> validation.map(i -> i + 1))); + } + +}