Как правильно реализовать это с помощью Vavr? - PullRequest
0 голосов
/ 09 января 2019

Я хотел бы получить ваш совет о том, как правильно написать этот код функциональным способом:

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 Ответ

0 голосов
/ 15 января 2019

Я бы написал 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>, я позволю вам адаптироваться к вашим бизнес-моделям.

...