Что касается использования Either
(doThing(...)
), то, похоже, вашего плоского отображения недостаточно.Я предполагаю, что вы хотите, чтобы ваше плоское отображение работало так же, как для Optional<T>
.
Картограф Optional.flatMap
принимает значение kind из T
и возвращает Optional<U>
, где U
является параметром универсального типа этого метода.Но Optional
имеет один параметр общего типа T
, тогда как Either
имеет два: A
и B
.Так что, если вы хотите отобразить карту Either<A,B> either
, недостаточно использовать одно отображение.
Одно отображение, что должно отображаться?«Значение, которое не null
», вы бы сказали, не так ли?Хорошо, но вы знаете это сначала во время выполнения.Ваш flatMap
метод определяется во время компиляции.Поэтому вы должны предоставить сопоставление для каждого случая.
Вы выбираете <C> Either<A, C> flatMap(Function<B, Either<A, C>> f)
.Это отображение использует значение типа B
в качестве ввода.Это означает, что если сопоставленный Either either
равен !either.isRight()
, все последующие сопоставления вернут Either.left(a)
, где a
- это значение самого первого Either.left(a)
.Так что на самом деле только Either either
, где either.isRight()
может быть сопоставлено с другим значением.И это должно быть either.isRight()
с самого начала.Это также означает, что после создания Either<A,B> either
все плоские отображения приведут к виду из Either<A,?>
.Таким образом, текущий flatMap
ограничивает Either either
сохранением своего левого универсального типа.Это то, что вы должны делать?
Если вы хотите создать плоскую карту Either either
без ограничений, вам нужны отображения для обоих случаев: either.isRight()
и !either.isRight()
.Это позволит вам продолжить плоское отображение в обоих направлениях.
Я сделал это следующим образом:
public class Either<A, B> {
public final A left;
public final B right;
private Either(A a, B b) {
left = a;
right = b;
}
public boolean isRight() {
return right != null;
}
@Override
public String toString() {
return isRight() ?
right.toString() :
left.toString();
}
public static <A, B> Either<A, B> left(A a) {
return new Either<>(a, null);
}
public static <A, B> Either<A, B> right(B b) {
return new Either<>(null, b);
}
public <C, D> Either<C, D> flatMap(Function<A, Either<C, D>> toLeft, Function<B, Either<C, D>> toRight) {
if (this.isRight()) {
return toRight.apply(this.right);
} else {
return toLeft.apply(this.left);
}
}
public static void main(String[] args) {
Either<String, String> left = Either.left(new Foo("left"))
.flatMap(l -> Either.right(new Bar(l.toString() + ".right")), r -> Either.left(new Baz(r.toString() + ".left")))
.flatMap(l -> Either.left(l.toString() + ".left"), r -> Either.right(r.toString() + ".right"));
System.out.println(left); // left.right.right
Either<String, String> right = Either.right(new Foo("right"))
.flatMap(l -> Either.right(new Bar(l.toString() + ".right")), r -> Either.left(new Baz(r.toString() + ".left")))
.flatMap(l -> Either.left(l.toString() + ".left"), r -> Either.right(r.toString() + ".right"))
.flatMap(l -> Either.right(l.toString() + ".right"), r -> Either.left(r.toString() + ".left"));
System.out.println(right); // right.left.left.right
}
private static class Foo {
private String s;
public Foo(String s) {
this.s = s;
}
@Override
public String toString() {
return s;
}
}
private static class Bar {
private String s;
public Bar(String s) {
this.s = s;
}
@Override
public String toString() {
return s;
}
}
private static class Baz {
private String s;
public Baz(String s) {
this.s = s;
}
@Override
public String toString() {
return s;
}
}
}
Отвечая на ваш вопрос: Да, возможно построить Либо, возвращая другой левыйзначение.Но я думаю, что вы намеревались узнать, как получить правильную работу Either
.