1

Been presented several situations where it'd have been cool to have Optional keeping references to previously mapped objects.

    // Normally the following is a good candidate for Optional fluent API (imagine also all the null checks that could be necessary)

    A a = new A();
    B b = calculateB(a);
    C c = calculateC(b)
    D d = calculateD(c)
    E e = calculateE(d)

    // As 
    E e = Optional.ofNullable(a)
            .map(this::calculateB)
    ...
    
    // But the following can't be expressed with Optional API
        A a = new A();
    B b = calculateB(a);
    C c = calculateC(a, b)
    D d = calculateD(a, c)
    E e = calculateE(a, d)

    // Something like the following sounds tempting
    PowerOptional.ofNullable(a)
            .mapAndKeep(this::calculateB) // mapAndKeep would carry the input as well to next call.
            .map((a, b) -> calculateC(a, b))
            .map((a, c) -> calculateD(a, c))
            .map((a, d) -> calculateE(a, d))
            .orElse(null);
    

This could improve readability of rather cumbersome methods. Is there a library that addresses this? Ideas (also on naming)?

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
juanmf
  • 2,002
  • 2
  • 26
  • 28
  • You can essentially do that by having optionals of tuples and then map from them to other tuples or entire lists of previous objects. – zapl Oct 11 '22 at 21:39
  • @zapl I thought of parameterBags but don’t feel it improves readability – juanmf Oct 11 '22 at 21:58
  • 2
    You just discovered the Monad: use `flatMap` as one answer already suggests. – michid Oct 13 '22 at 08:13

1 Answers1

5

It's just the flatMap method. It can be used for sequencing multiple operations that return Optionals, and it allows you to bind intermediate results to variables. In your case, it would be something like

Optional.ofNullable(nullableA)
  .flatMap(a -> calculateB(a)
    .flatMap(b -> calculateC(a, b)
      .flatMap(c -> calculateD(a, c)
        .flatMap(d -> calculateE(a, d)))))
  .orElse(null)

Note that not only a, but indeed all intermediate results become available in the later calculation steps. In an extreme case, it could look somewhat like this:

Optional.ofNullable(nullableA)
  .flatMap(a -> calculateB(a)
    .flatMap(b -> calculateC(a, b)
      .flatMap(c -> calculateD(a, b, c)
        .flatMap(d -> calculateE(a, b, c, d)))))
  .orElse(null)

i.e. every step could depend on the intermediate results from all previous steps.

Afaik, as of 2022, there is no special syntax for it in Java. The standard solution for avoiding the increasing indentation (popularized in Haskell) is some kind of do/for-yield-syntax, e.g. in Scala, that could be written vertically, and would resemble usual imperative syntax:

for
  a <- ofNullable(aNullable)
  b <- calculateB(a)
  c <- calculateC(a, b)
  d <- calculateD(a, c)
  e <- calculateE(a, d)
yield e
Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93