Как правильно реализовать это с помощью Vavr?
Я хотел бы получить ваш совет о том, как правильно написать этот код на функциональном пути:
private Option<CalcResult> calculate(Integer X, Integer Y) {
if (X < Y) return Option.none();
return Option.of( X + Y );
}
public Option<CalcResult> otherMethod(Obj o) {
if (o.getAttr()) {
// getA() & getB() are APIs out of my control and could return a null value
if (o.getA() != null && o.getB() != null) {
return calculate(o.getA(), o.getB());
}
}
return Option.none();
}
Рассчитать просто:
private Option<CalcResult> calculate(Integer X, Integer Y) {
return Option.when(X > Y, () -> X + Y);
}
За otherMethod
Это был мой первый подход:
public Option<CalcResult> otherMethod(Obj o) {
return Option.when(o.getAttr(), () ->
For(Option.of(o.getA()), Option.of(o.getB()))
.yield(this::calculate)
.toOption()
.flatMap(Function.identity())
).flatMap(Function.identity());
}
Но я чувствую, что код не так удобен для чтения, как я ожидал, по сравнению с первой версией (двойной flatMap
затрудняет понимание, на первый взгляд, почему это так)
Я попробовал с этим другим, который улучшает лекцию:
public Option<CalcResult> otherMethod(Obj o) {
return For(
Option.when(o.getAttr(), o::getAttr()),
Option.of(o.getA()),
Option.of(o.getB()))
.yield((__, x, y) -> this.calculate(x, y))
.toOption()
.flatMap(Function.identity());
}
Это более читабельно, но я думаю, что я не правильно использую Фор-понимание в этом случае.
Что бы вы посоветовали на этот случай? я правильно использую API vavr?
Спасибо
1 ответ
Я бы написал calculate
именно так, как вы это сделали (за исключением того, что я никогда не использовал бы верхний регистр для параметров:P).
Что касается otherMethod
Я бы использовал сопоставление с образцом. Сопоставление с образцом в Vavr даже не близко к PM в более функциональных языках, таких как Haskell, но я думаю, что оно по-прежнему правильно отражает ваше намерение:
public Option<Integer> otherMethod(Obj o) {
return Option.when(o.getAttr(), () -> Tuple(Option(o.getA()), Option(o.getB()))) //Option<Tuple2<Option<Integer>, Option<Integer>>>
.flatMap(ab -> Match(ab).option( // ab is a Tuple2<Option<Integer>, Option<Integer>>
Case($Tuple2($Some($()), $Some($())), () -> calculate(o.getA(), o.getB())) // Read this as "If the pair (A, B)" has the shape of 2 non-empty options, then calculate with what's inside
// Result of Match().option() is a Option<Option<Integer>>
).flatMap(identity())); // Option<Integer>
}
Альтернатива, без комментариев (обратите внимание, мы используем Match().of()
вместо Match().option()
так что мы ДОЛЖНЫ иметь дело со всеми возможными формами):
public Option<Integer> otherMethod(Obj o) {
return Option.when(o.getAttr(), () -> Tuple(Option(o.getA()), Option(o.getB())))
.flatMap(ab -> Match(ab).of(
Case($Tuple2($Some($()), $Some($())), () -> calculate(o.getA(), o.getB())),
Case($(), () -> None())
));
}
Я заменил CalcResult
с Integer
так как в вашем примере, calculate
действительно возвращает Option<Integer>
Я позволю вам адаптироваться к вашим бизнес-моделям.