package org.fungsi;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import static java.util.Objects.requireNonNull;
public interface Either<L, R> {
public static <L, R> Either<L, R> left(L left) { return new Left<>(left); }
public static <L, R> Either<L, R> right(R right) { return new Right<>(right); }
public static <T> Either<T, Throwable> success(T value) { return left(value); }
public static <T> Either<T, Throwable> failure(Throwable cause) { return right(cause); }
public static <T> T unsafe(Either<T, Throwable> e) {
return e.fold(Function.identity(), r -> {
throw Throwables.propagate(r);
});
}
public static <L, R> Either<L, R> of(Optional<L> left, Supplier<R> right) {
return left.<Either<L, R>>map(Either::left).orElseGet(() -> Either.right(right.get()));
}
public static <L, R> Either<L, R> of(Supplier<L> left, Optional<R> right) {
return right.<Either<L, R>>map(Either::right).orElseGet(() -> Either.left(left.get()));
}
public static <T> Either<T, Unit> of(Optional<T> option) {
return of(option, Unit.instance());
}
public static <T> Either<T, Unit> either(Optional<T> option) {
return of(option, Unit.instance());
}
public static <T> Either<T, Unit> ofNullable(T left) {
if (left == null) return Unit.right();
return Either.left(left);
}
L left();
boolean isLeft();
R right();
boolean isRight();
<LL, RR> Either<LL, RR> bind(Function<L, Either<LL, RR>> left, Function<R, Either<LL, RR>> right);
<T> T fold(Function<L, T> left, Function<R, T> right);
default <LL, RR> Either<LL, RR> map(Function<L, LL> left, Function<R, RR> right) {
return bind(left.andThen(Either::left), right.andThen(Either::right));
}
default <LL> Either<LL, R> leftFlatMap(Function<L, Either<LL, R>> left) {
return bind(left, Either::right);
}
default <LL> Either<LL, R> leftMap(Function<L, LL> left) {
return leftFlatMap(left.andThen(Either::left));
}
default Either<L, R> leftFilter(Supplier<R> otherwise, Predicate<L> fn) {
return leftFlatMap(l -> fn.test(l) ? left(l) : right(otherwise.get()));
}
default Either<L, R> leftFilter(R otherwise, Predicate<L> fn) {
return leftFilter(() -> otherwise, fn);
}
default Optional<L> leftOption() {
return this.<Optional<L>>fold(Optional::of, x -> Optional.empty());
}
default Either<L, R> leftFallback(Supplier<Either<L, R>> fn) {
return bind(Either::left, r -> fn.get());
}
default L leftOrElse(Supplier<L> fn) {
return fold(Function.identity(), r -> fn.get());
}
default L leftOrThrow(Supplier<Throwable> fn) {
return fold(Function.identity(), r -> {
throw Throwables.propagate(fn.get());
});
}
default <RR> Either<L, RR> rightFlatMap(Function<R, Either<L, RR>> right) {
return bind(Either::left, right);
}
default <RR> Either<L, RR> rightMap(Function<R, RR> right) {
return rightFlatMap(right.andThen(Either::right));
}
default Either<L, R> rightFilter(Supplier<L> otherwise, Predicate<R> fn) {
return rightFlatMap(r -> fn.test(r) ? right(r) : left(otherwise.get()));
}
default Either<L, R> rightFilter(L otherwise, Predicate<R> fn) {
return rightFilter(() -> otherwise, fn);
}
default Optional<R> rightOption() {
return this.<Optional<R>>fold(x -> Optional.empty(), Optional::of);
}
default Either<L, R> rightFallback(Supplier<Either<L, R>> fn) {
return bind(l -> fn.get(), Either::right);
}
default R rightOrElse(Supplier<R> fn) {
return fold(l -> fn.get(), Function.identity());
}
default R rightOrThrow(Supplier<Throwable> fn) {
return fold(l -> {
throw Throwables.propagate(fn.get());
}, Function.identity());
}
default Either<L, R> ifLeft(Consumer<L> fn) {
return leftFlatMap(l -> {
fn.accept(l);
return Either.this;
});
}
default Either<L, R> ifRight(Consumer<R> fn) {
return rightFlatMap(r -> {
fn.accept(r);
return Either.this;
});
}
default Either<R, L> swap() {
return bind(Either::right, Either::left);
}
default Matcher<L, R> match(Consumer<L> fn) {
return new Matcher<>(this, fn);
}
default <T> FoldToRight<R, T> foldLeft(Function<L, T> fn) {
return new Folder<L, R, T>(this).setLeft(fn);
}
default <T> FoldToLeft<L, T> foldRight(Function<R, T> fn) {
return new Folder<L, R, T>(this).setRight(fn);
}
static final class Left<L, R> implements Either<L, R> {
final L value;
Left(L value) {
this.value = value;
}
@Override
public L left() {
return value;
}
@Override
public boolean isLeft() {
return true;
}
@Override
public R right() {
throw new IllegalStateException();
}
@Override
public boolean isRight() {
return false;
}
@Override
public <LL, RR> Either<LL, RR> bind(Function<L, Either<LL, RR>> left, Function<R, Either<LL, RR>> right) {
return left.apply(value);
}
@Override
public <T> T fold(Function<L, T> left, Function<R, T> right) {
return left.apply(value);
}
}
static final class Right<L, R> implements Either<L, R> {
final R value;
Right(R value) {
this.value = value;
}
@Override
public L left() {
throw new IllegalStateException();
}
@Override
public boolean isLeft() {
return false;
}
@Override
public R right() {
return value;
}
@Override
public boolean isRight() {
return true;
}
@Override
public <LL, RR> Either<LL, RR> bind(Function<L, Either<LL, RR>> left, Function<R, Either<LL, RR>> right) {
return right.apply(value);
}
@Override
public <T> T fold(Function<L, T> left, Function<R, T> right) {
return right.apply(value);
}
}
@SuppressWarnings("UnusedParameters")
public static <L, R> Either<L, R> left(L left, Class<R> ignored) { return left(left); }
@SuppressWarnings("UnusedParameters")
public static <L, R> Either<L, R> right(Class<L> ignored, R right) { return right(right); }
public static final class Matcher<L, R> {
private final Either<L, R> value;
private final Consumer<L> left;
Matcher(Either<L, R> value, Consumer<L> left) {
this.value = value;
this.left = left;
}
public Either<L, R> then(Consumer<R> right) {
value.ifLeft(left);
value.ifRight(right);
return value;
}
}
public static interface FoldToLeft<L, T> {
T thenLeft(Function<L, T> fn);
}
public static interface FoldToRight<R, T> {
T thenRight(Function<R, T> fn);
}
public static final class Folder<L, R, T> implements FoldToLeft<L, T>, FoldToRight<R, T> {
private final Either<L, R> value;
private Function<L, T> left;
private Function<R, T> right;
Folder(Either<L, R> value) {
this.value = value;
}
Folder<L, R, T> setLeft(Function<L, T> left) {
this.left = left;
return this;
}
Folder<L, R, T> setRight(Function<R, T> right) {
this.right = right;
return this;
}
T proceed() {
return value.fold(requireNonNull(left, "left"), requireNonNull(right, "right"));
}
@Override
public T thenLeft(Function<L, T> fn) {
return setLeft(fn).proceed();
}
@Override
public T thenRight(Function<R, T> fn) {
return setRight(fn).proceed();
}
}
}