Search code examples
scalafunctional-programming

Scala Function Chaining and handle failure


I have many functions in my code defined with return type as Either[Throwable, String] and all of them have one argument of type String. Three representative functions of my code are defined as:

  val f1 = (input: String) => {
    /* Processing from the input using a library in my actual code returns a Either[Throwable, String] */
    if (input == "a") Left(new Exception(input))
    else Right("Success")
  }

  val f2 = (input: String) => {
    if (input == "b") Left(new Exception(input))
    else Right("Success")
  }

  val f3 = (input: String) => {
    if (input == "c") Left(new Exception(input))
    else Right("Success")
  }

To chain the function outputs, I'm writing code like:

  def x(input: String) = f1(input) match {
    case Left(value) => Left(value)
    case Right(_) => f2(input) match {
      case Left(value) => Left(value)
      case Right(_) => f3(input)
    } 
  }

Since this is just three functions so this might look like a short code. However there are multiple such matches that are happening in my code, so it's a very long code. I am looking to avoid such a chaining.

I know that Scala has a way to chain functions like f1.andThen(f2).andThen(f3), however the problem is that in each andThen we need to pass the same argument, in this case being input. However I want to chain these functions so that if there is a Left output, it should not go to the next andThen.

I believe this can be simplified using Functional Programming, but I don't know where to start. Is there a way we can achieve this using Scala functional programming?


Solution

  • If you have cats in scope, then all you need to do is this:

    import cats.syntax.all._
    
    val functions: List[String => Either[Throwable, Unit]] = List(
      // put your functions here.
    )
    
    val result: Either[Throwable, Unit] =
      functions.traverse_(f => f(input))
    

    Otherwise, you may emulate it using this:

    val init: Either[Throwable, Unit] = Right(())
    
    functions.foldLeft(init) {
      case (acc, f) =>
        acc.flatMap(_ => f(input))
    }