Add flatMapLeft/Right and getLeft/Right to Either (#128)
* clean up code * tree: replace deprecated assertj methods * either: add flatMapLeft and flatMapRight * either: add getLeft and getRight
This commit is contained in:
parent
d617ffdf3f
commit
30f903dbb2
7 changed files with 503 additions and 98 deletions
41
README.org
41
README.org
|
@ -1184,3 +1184,44 @@
|
|||
#+END_SRC
|
||||
|
||||
|
||||
*** =<T> Either<T, R> flatMapLeft(Function<L, Either<T, R>> f)=
|
||||
|
||||
FlatMap the function across the left value.
|
||||
|
||||
#+being_src java
|
||||
Either<Integer, String> either = Either.left(2);
|
||||
Either<Integer, String> resultLeft = either.flatMapLeft(l -> Either.left(l * 2));
|
||||
Either<Integer, String> resultRight = either.flatMapLeft(l -> Either.right(l * 2));
|
||||
#+end_src
|
||||
|
||||
|
||||
*** =<T> Either<T, R> flatMapRight(Function<L, Either<T, R>> f)=
|
||||
|
||||
FlatMap the function across the right value.
|
||||
|
||||
#+being_src java
|
||||
Either<Integer, String> either = Either.right("2");
|
||||
Either<Integer, String> resultLeft = either.flatMapRight(l -> Either.left(l * 2));
|
||||
Either<Integer, String> resultRight = either.flatMapRight(l -> Either.right(l * 2));
|
||||
#+end_src
|
||||
|
||||
|
||||
*** =Optional<L> getLeft()=
|
||||
|
||||
Returns an Optional containing the left value, if is a left, otherwise returns an empty Optional.
|
||||
|
||||
#+being_src java
|
||||
Either<Integer, String> either = Either.right("2");
|
||||
Optional<Integer> left = either.getLeft();
|
||||
#+end_src
|
||||
|
||||
|
||||
*** =Optional<R> getRight()=
|
||||
|
||||
Returns an Optional containing the right value, if is a right, otherwise returns an empty Optional.
|
||||
|
||||
#+being_src java
|
||||
Either<Integer, String> either = Either.right("2");
|
||||
Optional<String> right = either.getRight();
|
||||
#+end_src
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
package net.kemitix.mon.experimental.either;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
@ -31,6 +32,7 @@ import java.util.function.Function;
|
|||
* @param <R> the type of the Either for right value
|
||||
* @author Paul Campbell (pcampbell@kemitix.net)
|
||||
*/
|
||||
@SuppressWarnings("methodcount")
|
||||
public interface Either<L, R> {
|
||||
|
||||
/**
|
||||
|
@ -96,4 +98,38 @@ public interface Either<L, R> {
|
|||
* @return a new Either
|
||||
*/
|
||||
public abstract <T> Either<L, T> mapRight(Function<R, T> f);
|
||||
|
||||
/**
|
||||
* FlatMap the function across the left value.
|
||||
*
|
||||
* @param f the function to apply to any left value
|
||||
* @param <T> the type to change the left value to
|
||||
* @return a new Either if is a Left, else this
|
||||
*/
|
||||
public abstract <T> Either<T, R> flatMapLeft(Function<L, Either<T, R>> f);
|
||||
|
||||
/**
|
||||
* FlatMap the function across the right value.
|
||||
*
|
||||
* @param f the function to apply to any right value
|
||||
* @param <T> the type to change the right value to
|
||||
* @return a new Either if is a Right, else this
|
||||
*/
|
||||
public abstract <T> Either<L, T> flatMapRight(Function<R, Either<L, T>> f);
|
||||
|
||||
/**
|
||||
* Returns an Optional containing the left value, if is a left, otherwise
|
||||
* returns an empty Optional.
|
||||
*
|
||||
* @return An Optional containing any left value
|
||||
*/
|
||||
public abstract Optional<L> getLeft();
|
||||
|
||||
/**
|
||||
* Returns an Optional containing the right value, if is a right, otherwise
|
||||
* returns an empty Optional.
|
||||
*
|
||||
* @return An Optional containing any right value
|
||||
*/
|
||||
public abstract Optional<R> getRight();
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ package net.kemitix.mon.experimental.either;
|
|||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
@ -62,4 +63,25 @@ class Left<L, R> implements Either<L, R> {
|
|||
public <T> Either<L, T> mapRight(final Function<R, T> f) {
|
||||
return new Left<>(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Either<T, R> flatMapLeft(final Function<L, Either<T, R>> f) {
|
||||
return f.apply(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Either<L, T> flatMapRight(final Function<R, Either<L, T>> f) {
|
||||
return (Either<L, T>) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<L> getLeft() {
|
||||
return Optional.ofNullable(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<R> getRight() {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ package net.kemitix.mon.experimental.either;
|
|||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
@ -62,4 +63,26 @@ class Right<L, R> implements Either<L, R> {
|
|||
public <T> Either<L, T> mapRight(final Function<R, T> f) {
|
||||
return new Right<>(f.apply(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Either<T, R> flatMapLeft(final Function<L, Either<T, R>> f) {
|
||||
return (Either<T, R>) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Either<L, T> flatMapRight(final Function<R, Either<L, T>> f) {
|
||||
return f.apply(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<L> getLeft() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<R> getRight() {
|
||||
return Optional.ofNullable(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,10 +2,18 @@ package net.kemitix.mon;
|
|||
|
||||
import net.kemitix.mon.experimental.either.Either;
|
||||
import org.assertj.core.api.WithAssertions;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class EitherTest implements WithAssertions {
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
class EitherTest implements WithAssertions {
|
||||
@Nested
|
||||
@DisplayName("isLeft()")
|
||||
public class IsLeft {
|
||||
@Test
|
||||
void whenLeft_isLeft() {
|
||||
//when
|
||||
|
@ -13,15 +21,6 @@ class EitherTest implements WithAssertions {
|
|||
//then
|
||||
assertThat(either.isLeft()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenLeft_isNotRight() {
|
||||
//when
|
||||
final Either<Integer, String> either = Either.left(1);
|
||||
//then
|
||||
assertThat(either.isRight()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenRight_isNotLeft() {
|
||||
//when
|
||||
|
@ -29,7 +28,17 @@ class EitherTest implements WithAssertions {
|
|||
//then
|
||||
assertThat(either.isLeft()).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
@Nested
|
||||
@DisplayName("isRight()")
|
||||
public class IsRight {
|
||||
@Test
|
||||
void whenLeft_isNotRight() {
|
||||
//when
|
||||
final Either<Integer, String> either = Either.left(1);
|
||||
//then
|
||||
assertThat(either.isRight()).isFalse();
|
||||
}
|
||||
@Test
|
||||
void whenRight_isRight() {
|
||||
//when
|
||||
|
@ -37,7 +46,10 @@ class EitherTest implements WithAssertions {
|
|||
//then
|
||||
assertThat(either.isRight()).isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
@Nested
|
||||
@DisplayName("match()")
|
||||
public class Match {
|
||||
@Test
|
||||
void whenLeft_matchLeft() {
|
||||
//given
|
||||
|
@ -48,7 +60,6 @@ class EitherTest implements WithAssertions {
|
|||
right -> fail("Not a right")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenRight_matchRight() {
|
||||
//given
|
||||
|
@ -59,7 +70,10 @@ class EitherTest implements WithAssertions {
|
|||
right -> assertThat(right).isEqualTo("1")
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@Nested
|
||||
@DisplayName("mapLeft()")
|
||||
public class MapLeft {
|
||||
@Test
|
||||
void givenLeft_whenMapLeft_thenMap() {
|
||||
//given
|
||||
|
@ -72,33 +86,6 @@ class EitherTest implements WithAssertions {
|
|||
right -> fail("Not a right")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenRight_whenMapRight_thenMap() {
|
||||
//given
|
||||
final Either<Integer, String> either = Either.right("2");
|
||||
//when
|
||||
final Either<Integer, String> result = either.mapRight(l -> l + "2");
|
||||
//then
|
||||
result.match(
|
||||
left -> fail("Not a left"),
|
||||
right -> assertThat(right).isEqualTo("22")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenLeft_whenMapRight_thenDoNothing() {
|
||||
//given
|
||||
final Either<Integer, String> either = Either.left(2);
|
||||
//when
|
||||
final Either<Integer, String> result = either.mapRight(r -> r + "x");
|
||||
//then
|
||||
result.match(
|
||||
left -> assertThat(left).isEqualTo(2),
|
||||
right -> fail("Not a right")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenRight_whenMapLeft_thenDoNothing() {
|
||||
//given
|
||||
|
@ -111,4 +98,300 @@ class EitherTest implements WithAssertions {
|
|||
right -> assertThat(right).isEqualTo("2")
|
||||
);
|
||||
}
|
||||
}
|
||||
@Nested
|
||||
@DisplayName("mapRight()")
|
||||
public class MapRight {
|
||||
@Test
|
||||
void givenRight_whenMapRight_thenMap() {
|
||||
//given
|
||||
final Either<Integer, String> either = Either.right("2");
|
||||
//when
|
||||
final Either<Integer, String> result = either.mapRight(l -> l + "2");
|
||||
//then
|
||||
result.match(
|
||||
left -> fail("Not a left"),
|
||||
right -> assertThat(right).isEqualTo("22")
|
||||
);
|
||||
}
|
||||
@Test
|
||||
void givenLeft_whenMapRight_thenDoNothing() {
|
||||
//given
|
||||
final Either<Integer, String> either = Either.left(2);
|
||||
//when
|
||||
final Either<Integer, String> result = either.mapRight(r -> r + "x");
|
||||
//then
|
||||
result.match(
|
||||
left -> assertThat(left).isEqualTo(2),
|
||||
right -> fail("Not a right")
|
||||
);
|
||||
}
|
||||
}
|
||||
@Nested
|
||||
@DisplayName("flatMapLeft()")
|
||||
public class FlatMapLeft {
|
||||
@Test
|
||||
void givenLeft_whenFlatMapLeft_whenResultLeft_thenIsNewLeft() {
|
||||
//given
|
||||
Either<Integer, String> either = Either.left(2);
|
||||
//when
|
||||
Either<Integer, String> result = either.flatMapLeft(l -> Either.left(l * 2));
|
||||
//then
|
||||
result.match(
|
||||
left -> assertThat(left).isEqualTo(4),
|
||||
right -> fail("Not a right")
|
||||
);
|
||||
}
|
||||
@Test
|
||||
void givenLeft_whenFlatMapLeft_whenResultRight_thenIsRight() {
|
||||
//given
|
||||
Either<Integer, String> either = Either.left(2);
|
||||
//when
|
||||
Either<Integer, String> result = either.flatMapLeft(l -> Either.right("recovered"));
|
||||
//then
|
||||
result.match(
|
||||
left -> fail("Not a left"),
|
||||
right -> assertThat(right).isEqualTo("recovered")
|
||||
);
|
||||
}
|
||||
@Test
|
||||
void givenRight_whenFlatMapLeft_thenDoNothing() {
|
||||
//given
|
||||
Either<Integer, String> either = Either.right("2");
|
||||
//when
|
||||
Either<Integer, String> result = either.flatMapLeft(l -> Either.left(l * 2));
|
||||
//then
|
||||
result.match(
|
||||
left -> fail("Not a left"),
|
||||
right -> assertThat(right).isEqualTo("2")
|
||||
);
|
||||
}
|
||||
}
|
||||
@Nested
|
||||
@DisplayName("flatMapRight()")
|
||||
public class FlatMapRight {
|
||||
@Test
|
||||
void givenLeft_whenFlatMapRight_thenDoNothing() {
|
||||
//given
|
||||
Either<Integer, String> either = Either.left(2);
|
||||
//when
|
||||
Either<Integer, String> result = either.flatMapRight(l -> Either.right(l + "2"));
|
||||
//then
|
||||
result.match(
|
||||
left -> assertThat(left).isEqualTo(2),
|
||||
right -> fail("Not a right")
|
||||
);
|
||||
}
|
||||
@Test
|
||||
void givenRight_whenFlatMapRight_whenResultRight_thenIsNewRight() {
|
||||
//given
|
||||
Either<Integer, String> either = Either.right("2");
|
||||
//when
|
||||
Either<Integer, String> result = either.flatMapRight(l -> Either.right(l + "2"));
|
||||
//then
|
||||
result.match(
|
||||
left -> fail("Not a left"),
|
||||
right -> assertThat(right).isEqualTo("22")
|
||||
);
|
||||
}
|
||||
@Test
|
||||
void givenRight_whenFlatMapRight_whenResultLeft_thenIsLeft() {
|
||||
//given
|
||||
Either<Integer, String> either = Either.right("2");
|
||||
//when
|
||||
Either<Integer, String> result = either.flatMapRight(l -> Either.left(7));
|
||||
//then
|
||||
result.match(
|
||||
left -> assertThat(left).isEqualTo(7),
|
||||
right -> fail("Not a right")
|
||||
);
|
||||
}
|
||||
}
|
||||
@Nested @DisplayName("getLeft") public class GetLeft {
|
||||
@Test
|
||||
@DisplayName("")
|
||||
public void whenLeft_thenGetValue() {
|
||||
//given
|
||||
Either<String, Integer> either = Either.left("value");
|
||||
//when
|
||||
Optional<String> left = either.getLeft();
|
||||
//then
|
||||
assertThat(left).contains("value");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("")
|
||||
public void whenRight_thenGetEmpty() {
|
||||
//given
|
||||
Either<Integer, String> either = Either.right("value");
|
||||
//when
|
||||
Optional<Integer> left = either.getLeft();
|
||||
//then
|
||||
assertThat(left).isEmpty();
|
||||
}
|
||||
}
|
||||
@Nested @DisplayName("getRight") public class GetRight {
|
||||
@Test
|
||||
@DisplayName("")
|
||||
public void whenLeft_thenGetEmpty() {
|
||||
//given
|
||||
Either<String, Integer> either = Either.left("value");
|
||||
//when
|
||||
Optional<Integer> left = either.getRight();
|
||||
//then
|
||||
assertThat(left).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("")
|
||||
public void whenRight_thenGetValue() {
|
||||
//given
|
||||
Either<Integer, String> either = Either.right("value");
|
||||
//when
|
||||
Optional<String> left = either.getRight();
|
||||
//then
|
||||
assertThat(left).contains("value");
|
||||
}
|
||||
}
|
||||
// @Nested
|
||||
// @DisplayName("compose()")
|
||||
// public class Compose {
|
||||
//
|
||||
// BiFunction<String, String, Either<String, Integer>> leftToLeftComposer =
|
||||
// (la, lb) -> Either.left(la + lb);
|
||||
//
|
||||
// BiFunction<String, String, Either<String, Integer>> leftToRightComposer =
|
||||
// (la, lb) -> Either.right(Integer.parseInt(la) + Integer.parseInt(lb));
|
||||
//
|
||||
// BiFunction<Integer, Integer, Either<String, Integer>> rightToLeftComposer =
|
||||
// (la, lb) -> Either.left("" + la + lb);
|
||||
//
|
||||
// BiFunction<Integer, Integer, Either<String, Integer>> rightToRightComposer =
|
||||
// (la, lb) -> Either.right(la + lb);
|
||||
//
|
||||
// @Test
|
||||
// public void givenRightAndRight_whenComposeRight_then() {
|
||||
// //given
|
||||
// Either<String, Integer> ea = Either.right(3);
|
||||
// Either<String, Integer> eb = Either.right(7);
|
||||
// //when
|
||||
// Either<String, Integer> result = ea.compose(eb,
|
||||
// leftToRightComposer,
|
||||
// rightToRightComposer);
|
||||
// //then
|
||||
// result.match(
|
||||
// left -> fail("Not a left"),
|
||||
// right -> assertThat(right).isEqualTo(10)
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void givenRightAndRight_whenComposeLeft_then() {
|
||||
// //given
|
||||
// Either<String, Integer> ea = Either.right(3);
|
||||
// Either<String, Integer> eb = Either.right(7);
|
||||
// //when
|
||||
// Either<String, Integer> result = ea.compose(eb,
|
||||
// leftToLeftComposer,
|
||||
// rightToLeftComposer);
|
||||
// //then
|
||||
// result.match(
|
||||
// left -> assertThat(left).isEqualTo("a+b"),
|
||||
// right -> fail("Not a right")
|
||||
// );
|
||||
// }
|
||||
// // compose left with left
|
||||
// @Test
|
||||
// public void givenLeftAndLeft_whenComposeRight_then() {
|
||||
// //given
|
||||
// Either<String, Integer> ea = Either.left("3");
|
||||
// Either<String, Integer> eb = Either.left("7");
|
||||
// //when
|
||||
// Either<String, Integer> result = ea.compose(eb,
|
||||
// leftToRightComposer,
|
||||
// rightToRightComposer);
|
||||
// //then
|
||||
// result.match(
|
||||
// left -> fail("Not a left"),
|
||||
// right -> assertThat(right).isEqualTo(10)
|
||||
// );
|
||||
// }
|
||||
// @Test
|
||||
// public void givenLeftAndLeft_whenComposeLeft_then() {
|
||||
// //given
|
||||
// Either<String, Integer> ea = Either.left("3");
|
||||
// Either<String, Integer> eb = Either.left("7");
|
||||
// //when
|
||||
// Either<String, Integer> result = ea.compose(eb,
|
||||
// leftToLeftComposer,
|
||||
// rightToLeftComposer);
|
||||
// //then
|
||||
// result.match(
|
||||
// left -> fail("Not a left"),
|
||||
// right -> assertThat(right).isEqualTo(10)
|
||||
// );
|
||||
// }
|
||||
// // compose left with right
|
||||
// @Test
|
||||
// public void givenLeftAndRight_whenComposeRight_then() {
|
||||
// //given
|
||||
// Either<String, Integer> ea = Either.left("3");
|
||||
// Either<String, Integer> eb = Either.right(7);
|
||||
// //when
|
||||
// Either<String, Integer> result = ea.compose(eb,
|
||||
// leftToRightComposer,
|
||||
// rightToRightComposer);
|
||||
// //then
|
||||
// result.match(
|
||||
// left -> fail("Not a left"),
|
||||
// right -> assertThat(right).isEqualTo(10)
|
||||
// );
|
||||
// }
|
||||
// @Test
|
||||
// public void givenLeftAndRight_whenComposeLeft_then() {
|
||||
// //given
|
||||
// Either<String, Integer> ea = Either.left("3");
|
||||
// Either<String, Integer> eb = Either.right(7);
|
||||
// //when
|
||||
// Either<String, Integer> result = ea.compose(eb,
|
||||
// leftToLeftComposer,
|
||||
// rightToLeftComposer);
|
||||
// //then
|
||||
// result.match(
|
||||
// left -> fail("Not a left"),
|
||||
// right -> assertThat(right).isEqualTo(10)
|
||||
// );
|
||||
// }
|
||||
// // compose right with left
|
||||
// @Test
|
||||
// public void givenRightAndLeft_whenComposeRight_then() {
|
||||
// //given
|
||||
// Either<String, Integer> ea = Either.right(3);
|
||||
// Either<String, Integer> eb = Either.left("7");
|
||||
// //when
|
||||
// Either<String, Integer> result = ea.compose(eb,
|
||||
// leftToRightComposer,
|
||||
// rightToRightComposer);
|
||||
// //then
|
||||
// result.match(
|
||||
// left -> fail("Not a left"),
|
||||
// right -> assertThat(right).isEqualTo(10)
|
||||
// );
|
||||
// }
|
||||
// @Test
|
||||
// public void givenRightAndLeft_whenComposeLeft_then() {
|
||||
// //given
|
||||
// Either<String, Integer> ea = Either.right(3);
|
||||
// Either<String, Integer> eb = Either.left("7");
|
||||
// //when
|
||||
// Either<String, Integer> result = ea.compose(eb,
|
||||
// leftToLeftComposer,
|
||||
// rightToLeftComposer);
|
||||
// //then
|
||||
// result.match(
|
||||
// left -> fail("Not a left"),
|
||||
// right -> assertThat(right).isEqualTo(10)
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ class MutableTreeTest implements WithAssertions {
|
|||
asList(
|
||||
MutableTree.leaf(sid2),
|
||||
MutableTree.leaf(sid3)));
|
||||
assertThat(result).isEqualToComparingFieldByFieldRecursively(expectedTree);
|
||||
assertThat(result).usingRecursiveComparison().isEqualTo(expectedTree);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -65,7 +65,7 @@ class TreeBuilderTest {
|
|||
final Tree<Node> result = rootBuilder.build();
|
||||
//then
|
||||
assertThat(result.count()).isEqualTo(3);
|
||||
assertThat(result).isEqualToComparingFieldByFieldRecursively(
|
||||
assertThat(result).usingRecursiveComparison().isEqualTo(
|
||||
MutableTree.of(rootNode, Collections.singleton(
|
||||
MutableTree.of(childNode, Collections.singleton(
|
||||
MutableTree.leaf(grandchildNode))))));
|
||||
|
@ -86,7 +86,7 @@ class TreeBuilderTest {
|
|||
.build();
|
||||
//then
|
||||
assertThat(result.count()).isEqualTo(4);
|
||||
assertThat(result).isEqualToComparingFieldByFieldRecursively(
|
||||
assertThat(result).usingRecursiveComparison().isEqualTo(
|
||||
MutableTree.of(rootNode, asList(
|
||||
MutableTree.leaf(child1Node),
|
||||
MutableTree.leaf(child2Node),
|
||||
|
|
Loading…
Reference in a new issue