Add Either (experimental)

This commit is contained in:
Paul Campbell 2018-07-16 18:49:44 +01:00
parent a85280341a
commit e5958ba432
6 changed files with 359 additions and 0 deletions

View file

@ -2,6 +2,7 @@ CHANGELOG
=========
0.11.0
------
* Rename `Result.maybeThen()` as `Result.flatMapMaybe()`
* Add `Maybe.match(Consumer,Runnable)`
@ -9,6 +10,7 @@ CHANGELOG
* Add `Maybe.isNothing()`
* BUG: `Maybe.orElseThrow()` now returns value when a Just
* Rewrite README
* Add `Either` (experimental)
0.10.0
------

View file

@ -0,0 +1,99 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2017 Paul Campbell
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package net.kemitix.mon.experimental.either;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* An Either type for holding a one of two possible values, a left and a right, that may be of different types.
*
* @param <L> the type of the Either for left value
* @param <R> the type of the Either for right value
* @author Paul Campbell (pcampbell@kemitix.net)
*/
public interface Either<L, R> {
/**
* Create a new Either holding a left value.
*
* @param l the left value
* @param <L> the type of the left value
* @param <R> the type of the right value
* @return a Either holding the left value
*/
static <L, R> Either<L, R> left(final L l) {
return new Left<>(l);
}
/**
* Create a new Either holding a right value.
*
* @param r the right value
* @param <L> the type of the left value
* @param <R> the type of the right value
* @return a Either holding the right value
*/
static <L, R> Either<L, R> right(final R r) {
return new Right<>(r);
}
/**
* Checks if the Either holds a left value.
*
* @return true if this Either is a left
*/
boolean isLeft();
/**
* Checks if the Either holds a right value.
*
* @return true if this Either is a right
*/
boolean isRight();
/**
* Matches the Either, invoking the correct Consumer.
*
* @param onLeft the Consumer to invoke when the Either is a left
* @param onRight the Consumer to invoke when the Either is a right
*/
void match(Consumer<L> onLeft, Consumer<R> onRight);
/**
* Map 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
*/
<T> Either<T, R> mapLeft(Function<L, T> f);
/**
* Map 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
*/
<T> Either<L, T> mapRight(Function<R, T> f);
}

View file

@ -0,0 +1,61 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2017 Paul Campbell
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package net.kemitix.mon.experimental.either;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* An Either type holding a left value.
*
* @param <L> the type of the Either for left value
* @param <R> the type of the Either for right value
* @author Paul Campbell (pcampbell@kemitix.net)
*/
@RequiredArgsConstructor
class Left<L, R> implements Either<L, R> {
@Getter
private final boolean left = true;
@Getter
private final boolean right = false;
private final L value;
@Override
public void match(final Consumer<L> onLeft, final Consumer<R> onRight) {
onLeft.accept(value);
}
@Override
public <T> Either<T, R> mapLeft(final Function<L, T> f) {
return new Left<>(f.apply(value));
}
@Override
public <T> Either<L, T> mapRight(final Function<R, T> f) {
return new Left<>(value);
}
}

View file

@ -0,0 +1,61 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2017 Paul Campbell
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package net.kemitix.mon.experimental.either;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* An Either type holding a right value.
*
* @param <L> the type of the Either for left value
* @param <R> the type of the Either for right value
* @author Paul Campbell (pcampbell@kemitix.net)
*/
@RequiredArgsConstructor
class Right<L, R> implements Either<L, R> {
@Getter
private final boolean left = false;
@Getter
private final boolean right = true;
private final R value;
@Override
public void match(final Consumer<L> onLeft, final Consumer<R> onRight) {
onRight.accept(value);
}
@Override
public <T> Either<T, R> mapLeft(final Function<L, T> f) {
return new Right<>(value);
}
@Override
public <T> Either<L, T> mapRight(final Function<R, T> f) {
return new Right<>(f.apply(value));
}
}

View file

@ -0,0 +1,22 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2017 Paul Campbell
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package net.kemitix.mon.experimental.either;

View file

@ -0,0 +1,114 @@
package net.kemitix.mon;
import net.kemitix.mon.experimental.either.Either;
import org.assertj.core.api.WithAssertions;
import org.junit.Test;
public class EitherTest implements WithAssertions {
@Test
public void whenLeft_isLeft() {
//when
final Either<Integer, String> either = Either.left(1);
//then
assertThat(either.isLeft()).isTrue();
}
@Test
public void whenLeft_isNotRight() {
//when
final Either<Integer, String> either = Either.left(1);
//then
assertThat(either.isRight()).isFalse();
}
@Test
public void whenRight_isNotLeft() {
//when
final Either<Integer, String> either = Either.right("1");
//then
assertThat(either.isLeft()).isFalse();
}
@Test
public void whenRight_isRight() {
//when
final Either<Integer, String> either = Either.right("1");
//then
assertThat(either.isRight()).isTrue();
}
@Test
public void whenLeft_matchLeft() {
//given
final Either<Integer, String> either = Either.left(1);
//then
either.match(
left -> assertThat(left).isEqualTo(1),
right -> fail("Not a right")
);
}
@Test
public void whenRight_matchRight() {
//given
final Either<Integer, String> either = Either.right("1");
//then
either.match(
left -> fail("Not a left"),
right -> assertThat(right).isEqualTo("1")
);
}
@Test
public void givenLeft_whenMapLeft_thenMap() {
//given
final Either<Integer, String> either = Either.left(2);
//when
final Either<Integer, String> result = either.mapLeft(l -> l * 2);
//then
result.match(
left -> assertThat(left).isEqualTo(4),
right -> fail("Not a right")
);
}
@Test
public 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
public 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
public void givenRight_whenMapLeft_thenDoNothing() {
//given
final Either<Integer, String> either = Either.right("2");
//when
final Either<Integer, String> result = either.mapLeft(l -> l * 2);
//then
result.match(
left -> fail("Not a left"),
right -> assertThat(right).isEqualTo("2")
);
}
}