Skip to content

Commit adf52a1

Browse files
Changed Either.sequence return type (#2072)
1 parent 3db51f8 commit adf52a1

File tree

2 files changed

+64
-32
lines changed

2 files changed

+64
-32
lines changed

vavr/src/main/java/io/vavr/control/Either.java

+32-18
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import io.vavr.Value;
1010
import io.vavr.collection.Iterator;
1111
import io.vavr.collection.Seq;
12-
import io.vavr.collection.Vector;
1312

1413
import java.io.Serializable;
1514
import java.util.NoSuchElementException;
@@ -166,25 +165,40 @@ default <U> U fold(Function<? super L, ? extends U> leftMapper, Function<? super
166165

167166
/**
168167
* Reduces many {@code Either}s into a single {@code Either} by transforming an
169-
* {@code Iterable<Either<? extends L, ? extends R>>} into a {@code Either<? extends L, Seq<R>>}. If any of
170-
* the {@code Either}s are {@link Either.Left}, then this returns a {@link Either.Left}.
168+
* {@code Iterable<Either<L, R>>} into a {@code Either<Seq<L>, Seq<R>>}.
169+
* <p>
170+
* If any of the given {@code Either}s is a {@link Either.Left} then sequence returns a {@link Either.Left}
171+
* containing a non-empty {@link Seq} of all left values.
172+
* <p>
173+
* If none of the given {@code Either}s is a {@link Either.Left} then sequence returns a {@link Either.Right}
174+
* containing a (possibly empty) {@link Seq} of all right values.
175+
*
176+
* <pre>{@code
177+
* // = Right(Seq())
178+
* Either.sequence(List.empty())
179+
*
180+
* // = Right(Seq(1, 2))
181+
* Either.sequence(List.of(Either.right(1), Either.right(2)))
171182
*
172-
* @param values An {@link Iterable} of {@code Either}s
173-
* @param <L> left type of the Eithers
174-
* @param <R> right type of the Eithers
175-
* @return A {@code Either} of a {@link Seq} of results
176-
* @throws NullPointerException if {@code values} is null
183+
* // = Left(Seq("x"))
184+
* Either.sequence(List.of(Either.right(1), Either.left("x")))
185+
* }</pre>
186+
*
187+
* @param eithers An {@link Iterable} of {@code Either}s
188+
* @param <L> closure of all left types of the given {@code Either}s
189+
* @param <R> closure of all right types of the given {@code Either}s
190+
* @return An {@code Either} of a {@link Seq} of left or right values
191+
* @throws NullPointerException if {@code eithers} is null
177192
*/
178-
static <L,R> Either<L, Seq<R>> sequence(Iterable<? extends Either<? extends L, ? extends R>> values) {
179-
Objects.requireNonNull(values, "values is null");
180-
Vector<R> vector = Vector.empty();
181-
for (Either<? extends L, ? extends R> value : values) {
182-
if (value.isLeft()) {
183-
return Either.left(value.getLeft());
184-
}
185-
vector = vector.append(value.get());
186-
}
187-
return Either.right(vector);
193+
@SuppressWarnings("unchecked")
194+
static <L,R> Either<Seq<L>, Seq<R>> sequence(Iterable<? extends Either<? extends L, ? extends R>> eithers) {
195+
Objects.requireNonNull(eithers, "eithers is null");
196+
return Iterator.ofAll((Iterable<Either<L, R>>) eithers)
197+
.partition(Either::isLeft)
198+
.apply((leftPartition, rightPartition) -> leftPartition.hasNext()
199+
? Either.left(leftPartition.map(Either::getLeft).toVector())
200+
: Either.right(rightPartition.map(Either::get).toVector())
201+
);
188202
}
189203

190204
/**

vavr/src/test/java/io/vavr/control/EitherTest.java

+32-14
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import io.vavr.AbstractValueTest;
1010
import io.vavr.collection.Seq;
1111
import io.vavr.collection.List;
12+
import io.vavr.collection.Vector;
1213
import org.junit.Test;
1314

1415
import java.util.NoSuchElementException;
@@ -17,6 +18,7 @@
1718

1819
import static io.vavr.API.Left;
1920
import static io.vavr.API.Right;
21+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2022

2123
public class EitherTest extends AbstractValueTest {
2224

@@ -93,26 +95,42 @@ public void shouldFoldRight() {
9395
// -- sequence
9496

9597
@Test
96-
public void shouldConvertListOfRightToEitherOfList() {
97-
List<Either<String, String>> tries = List.of(Either.right("a"), Either.right("b"), Either.right("c"));
98-
Either<String, Seq<String>> reducedEither = Either.sequence(tries);
99-
assertThat(reducedEither instanceof Either.Right).isTrue();
100-
assertThat(reducedEither.get().size()).isEqualTo(3);
101-
assertThat(reducedEither.get().mkString()).isEqualTo("abc");
98+
public void shouldThrowWhenSequencingNull() {
99+
assertThatThrownBy(() -> Either.sequence(null))
100+
.isInstanceOf(NullPointerException.class)
101+
.withFailMessage("eithers is null");
102102
}
103103

104104
@Test
105-
public void shouldConvertListOfLeftToEitherOfList() {
106-
List<Either<Integer, String>> tries = List.of(Either.left(1), Either.left(2), Either.left(3));
107-
Either<Integer, Seq<String>> reducedEither = Either.sequence(tries);
108-
assertThat(reducedEither).isEqualTo(Either.left(1));
105+
public void shouldSequenceEmptyIterableOfEither() {
106+
final Iterable<Either<Integer, String>> eithers = List.empty();
107+
final Either<Seq<Integer>, Seq<String>> actual = Either.sequence(eithers);
108+
final Either<Seq<Integer>, Seq<String>> expected = Either.right(Vector.empty());
109+
assertThat(actual).isEqualTo(expected);
110+
}
111+
112+
@Test
113+
public void shouldSequenceNonEmptyIterableOfRight() {
114+
final Iterable<Either<Integer, String>> eithers = List.of(Either.right("a"), Either.right("b"), Either.right("c"));
115+
final Either<Seq<Integer>, Seq<String>> actual = Either.sequence(eithers);
116+
final Either<Seq<Integer>, Seq<String>> expected = Either.right(Vector.of("a", "b", "c"));
117+
assertThat(actual).isEqualTo(expected);
109118
}
110119

111120
@Test
112-
public void shouldConvertListOfMixedEitherToEitherOfList() {
113-
List<Either<Integer,String>> tries = List.of(Either.right("a"), Either.left(1), Either.right("c"));
114-
Either<Integer, Seq<String>> reducedEither = Either.sequence(tries);
115-
assertThat(reducedEither).isEqualTo(Either.left(1));
121+
public void shouldSequenceNonEmptyIterableOfLeft() {
122+
final Iterable<Either<Integer, String>> eithers = List.of(Either.left(1), Either.left(2), Either.left(3));
123+
final Either<Seq<Integer>, Seq<String>> actual = Either.sequence(eithers);
124+
final Either<Seq<Integer>, Seq<String>> expected = Either.left(Vector.of(1, 2, 3));
125+
assertThat(actual).isEqualTo(expected);
126+
}
127+
128+
@Test
129+
public void shouldSequenceNonEmptyIterableOfMixedEither() {
130+
final Iterable<Either<Integer,String>> eithers = List.of(Either.right("a"), Either.left(1), Either.right("c"), Either.left(3));
131+
final Either<Seq<Integer>, Seq<String>> actual = Either.sequence(eithers);
132+
final Either<Seq<Integer>, Seq<String>> expected = Either.left(Vector.of(1, 3));
133+
assertThat(actual).isEqualTo(expected);
116134
}
117135

118136
@Test

0 commit comments

Comments
 (0)