Suppose we have 3 methods which all return an Option
import scala.util.Try
def divideSafe(d: Int): Option[Int] = Try(42 / d).toOption
def sqrtSafe(x: Double): Option[Double] = if(!math.sqrt(x).isNaN) Some(math.sqrt(x)) else None
def convertSafe(s: String): Option[Int] = Try(s.toInt).toOption
Now I want to chain them while a condition holds. In my case, if the result from the previous method is defined then should go to the next one. And as long as the condition is false, then stop the operation and return a default value.
I can obtain the desired result with nested if-else statements:
def superSafe(d: Int, x: Double, s: String): Unit = {
  if (divideSafe(d).isDefined) {
    println(divideSafe(d).get.toString)
    if (sqrtSafe(x).isDefined) {
      println(sqrtSafe(x).toString)
      if (convertSafe(s).isDefined) {
        println(convertSafe(s).get.toString)
      } else {
        println("Converting failed")
      }
    } else {
      println("Sqrt failed")
    }
  } else {
    println("Dividing failed")
  }
}
Therefore:
superSafe(1, -1, "5") would print 42 and Sqrt failed
superSafe(0, 4, "cat") would print Dividing failed
superSafe(42, 4, 1) would print 1,2,1
However I want to avoid the nested if-else statements and I am interested if there is a functional way in Scala to solve this kind of problems.
I want something like orElse statement, but the other way around.
The desired function could be used like this:
divideSafe(42) ifThen("Dividing failed") sqrtSafe(-4) ifThen("Sqrt failed") convertSafe("cat") ifThen("Converting failed")
                You're using Option which is not best suited for your use case. I believe Either would work better in your example because it can hold information about failure(Left) or about success (Right). If you rewrote your functions to use Either it might look like:
def divideSafe(d: Int): Either[String, Int] = Try(42 / d).toOption.toRight("Divide failed")
def sqrtSafe(x: Double): Either[String, Double] = math.sqrt(x) match {
   case s if s.isNaN => Left("Sqrt failed") //you can use pattern matching to calculate square just once
   case s => Right(s)
} 
def convertSafe(s: String): Either[String, Int] = Try(s.toInt).toOption.toRight("Converting failed")
And then you could use for comprehension in your superSafe method:
def superSafe(d: Int, x: Double, s: String): Unit = {
  val r: Either[String, (Int, Double, Int)] = for { //for-comprehension shortcircuits
    r1 <- divideSafe(d) //if divideSafe fails it whole comprehension will return Left(Divide failed)
    r2 <- sqrtSafe(x) //if it succeds it will go further
    r3 <- convertSafe(s)
  } yield (r1,r2,r3)
  r match { 
    case Right((r1,r2,r3)) => { //you could move it inside yield, I splitted it for clarity
      println(r1)
      println(r2)
      println(r3)
    }
    case Left(e) => println(e)
  }
}
                        If you like it more functional, you can try this:
import scala.util.Try
def divideSafe(d: Int): Option[Int] = Try(42 / d).toOption
def sqrtSafe(x: Double): Option[Double] = if(!math.sqrt(x).isNaN) Some(math.sqrt(x)) else None
def convertSafe(s: String): Option[Int] = Try(s.toInt).toOption
def superSafe(d: Int, x: Double, s: String): Any = {
  divideSafe(d)
    .map(rd => sqrtSafe(x)
    .map(rx => convertSafe(s)
    .map(rs => (rd, rx, rs))
    .getOrElse((rd, rx, "Converting failed")))
    .getOrElse((rd, "Sqrt failed")))
    .getOrElse("Dividing failed")
}
println(superSafe(1, 1, "1"))
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With