Kity Cartman
Kity Cartman

Reputation: 896

How to return different results from Futures in Scala without ending up in nested if statements

I am actually looking for a better Scala construct when it comes to return different results from Future on different conditions.

Lets say, inside a Future, I need to do certain validations before return a successful result. There are two ways to do so.

Way I:

Future {
    if(!validation1()) return Future.failed("Validation1 failed!")
    if(!validation2()) return Future.failed("Validation2 failed!")
    if(!validation3()) return Future.failed("Validation3 failed!")

    Future.successful()
}.flatMap(identity)

Way II:

Future {
    if(!validation1())
        Future.failed("Validation1 failed!")
    else {
        if(!validation2())
            Future.failed("Validation2 failed!")
        else {
            if(!validation3())
                Future.failed("Validation3 failed!")
            else {
                Future.successful("Results")                
            }
        }
    }
}.flatMap(identity)

But there is a problem with the Way I, it results in exception scala.runtime.NonLocalReturnControl. You can find the explaination in this link: https://tpolecat.github.io/2014/05/09/return.html

Which leaves us with Way II. The problem with this construct though is that it soon turns ugly with more number of validations.

Can anyone suggest a better way to express it?

Please let me know if this is the right place for this question or this question needs to be expressed in a better way.

TIA.

Upvotes: 4

Views: 204

Answers (2)

Epicurist
Epicurist

Reputation: 923

What about:

def validation1(): (Boolean, String) // Tuple with e.g. (testResult, "Validation1 failed!")

def validation2(): (Boolean, String)

def validation3(): (Boolean, String)

val firstFound = Seq(validation1(), validation2(), validation3()).find(_._1)

if (firstFound.isEmpty) Success("Results") else Failure(firstFound.map{_._2}.get)

Upvotes: 0

Nazarii Bardiuk
Nazarii Bardiuk

Reputation: 4342

Consider to extract each validation into separate Future

def val1():Furure[String] = 
  if validation1() Future.failed("Validation1 failed") else Future.successful("Result")

def val2():Furure[String] = 
  if validation2() Future.failed("Validation2 failed") else Future.successful("Result")

def val3():Furure[String] = 
  if validation3() Future.failed("Validation3 failed") else Future.successful("Result")

and then chain them sequentially using flatMap

val1().flatMap(_ => val2()).flatMap(_ => val3())

or with syntactic sugar

for {
  _      <- val1()
  _      <- val2()
  result <- val3()
} yield result

Upvotes: 4

Related Questions