6

Let's say that I have function Object f(String a, String b) and I want to call two different functions that return Optional Strings to get the parameters for f Optional<String> getA() and Optional<String> getB(). I can think of two solutions but neither look all that clean, especially when you have even more parameters:

1:

return getA().flatMap(
    a -> getB().map(
        b -> f(a,b)).get()

2:

Optional<String> a = getA();
Optional<String> b = getB();
if(a.isPresent() && b.isPresent()) {
    return f(a.get(), b.get());
}

Is there a cleaner way to do this?

3
  • 3
    definetly prefer the second way, the first one is not understandable and a functional mess Commented Mar 5, 2019 at 16:47
  • 2
    The fact that you are using get() in the first means that you'll get an Exception if either Optional is empty, so definitely not the first method. Commented Mar 5, 2019 at 16:52
  • 1
    Curious to know, what is the else part of (2) which is what could have been an orElse.. candidate for (1). This is an unclear problem and is driving opinions... and another question that puzzles me at least is, what does the method f do such that you need to ensure that both(/all) the values are present before calling it? Just wondering could f accept a varargs such as Object f(String... args)? Commented Mar 5, 2019 at 17:43

4 Answers 4

4

You've just stumbled upon a concept called lifting in functional programming, that enables you to lift regular functions (e.g. A -> B) into new domains (e.g. Optional<A> -> Optional<B>).

There's also a syntactic sugar for flatMapping and mapping more comfortably called the do notation in Haskell and similar languages, and for comprehension in Scala. It gives you a way to keep the flow linear and avoid nesting (that you were forced to go through in your example 1).

Java, unfortunately has nothing of the sort, as its functional programming capabilities are meager, and even Optional isn't really a first-class citizen (no standard API actually uses it). So you're stuck with the approaches you've already discovered.

In case you're curious about the concepts mentioned above, read on.

Lifting

Assuming you have:

public String f(A a, B b) {
    return b + "-" + a;
}

With its Scala equivalent:

def f(a: A, b: B) = b + "-" + a

Lifting f into Option (same as Optional in Java) would look like this (using Scalaz library, see here for Cats)

val lifted = Monad[Option].lift2(f)

lifted is now a function equivalent to:

public Optional<String> f(Optional<A> a, Optional<B> b) {
    if(a.isPresent() && b.isPresent()) {
        return Optional.of(b + "-" + a);
    }
    return  Optional.empty;
}

Exactly what you're looking for, in 1 line, and works for any context (e.g. List, not just Option) and any function.

For comprehension / Do notation

Using for comprehension, your example would look like this (I think, my Scala is weak):

for { 
    a <- getA();
    b <- getB();
} yield f(a, b)

And again, this is applicable to anything that can be flatMapped over, like List, Future etc.

Sign up to request clarification or add additional context in comments.

Comments

3

You could stream the arguments and apply the condition only once, but whether or not this is more elegant than your solutions is in the eye of the beholder:

if (Stream.of(a, b).allMatch(Optional::isPresent)) {
    return f(a.get(), b.get());
}

1 Comment

You still need the first two lines of #2, so how exactly is this better? It's just longer.
1

If you are sure that a and b are both present (as your final call to get in solution 1 seems to suggest), I think it is pretty straightforward:

    return f(getA().orElseThrow(() -> new NoSuchElementException("a not present")),
             getB().orElseThrow(() -> new NoSuchElementException("b not present")));

If you aren’t sure that both are present, I would prefer your solution 1. It exploits Optional the best. Only I would not call get at the end, but rather orElse or what makes sense in your situation, for example:

    return getA()
            .flatMap(a -> getB().map(b -> f(a,b)))
            .orElse("Not both present");

Comments

1

I'm of the opinion that if there is no good way to use Optional, then there is no reason to try to use it anyway.

I find this to be cleaner and simpler than your option 2:

String a = getA().orElse(null);
String b = getB().orElse(null);
if(a != null && b != null) {
    return f(a, b);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.