Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala Future error handling with Either

I am writing a wrapper for an API and I want to do error handling for applications problems. Each request returns a Future so in order to do this I see 2 options: using a Future[Either] or using exceptions to fail the future immediately.

Here is a snippet with both situations, response is a future with the return of the HTTP request:

  def handleRequestEither: Future[Either[String, String]] = {
    response.map {
      case "good_string" => Right("Success")
      case _ => Left("Failed")
    }
  }

  def handleRequest: Future[String] = {
    response.map {
      case "good_string" => "Success"
      case _ => throw new Exception("Failed")
    }
  }

And here is the snippet to get the result in both cases:

handleRequestEither.onComplete {
  case Success(res) =>
    res match {
      case Right(rightRes) => println(s"Success $res")
      case Left(leftRes) => println(s"Failure $res")
    }
  case Failure(ex) =>
    println(s"Failure $ex")
}

handleRequest.onComplete {
  case Success(res) => println(s"Success $res")
  case Failure(ex) => println(s"Failure $ex")
}

I don't like to use exceptions, but using Future[Either] makes it much more verbose to get the response afterwards, and if I want to map the result into another object it gets even more complicated. Is this the way to go, or are there better alternatives?

like image 783
nmat Avatar asked Sep 30 '15 12:09

nmat


1 Answers

Let me paraphrase Erik Meijer and consider the following table:

enter image description here

Consider then this two features of a language construct: arity (does it aggregate one or many items?) and mode (synchronous when blocking read operations until ready or asynchronous when not).

All of this imply that Try constructs and blocks manage the success or failure of the block generating the result synchronously. You'll control whether your resources provides the right answer without encountering problems (those described by exceptions).

On the other hand a Future is a kind of asynchronous Try. That means that it successfully completes when no problems (exceptions) has been found then notifying its subscribers. Hence, I don't think you should have a future of Either in this case, that is your second handleRequest implementation is the right way of using futures.

Finally, if what disturbs you is throwing an exception, you could follow the approach of Promises:

def handleRequest: Future[String] = {
   val p = Promise[String]
   response.map {
      case "good_string" => p.success("Success")
      case _ => p.failure(new Exception("Failed"))
    }
   p.future
}

Or:

case class Reason(msg: String) extends Exception

def handleRequest: Future[String] = {
   val p = Promise[String]
   response.map {
      case "good_string" => p.success("Success")
      case _ => p.failure(Reason("Invalid response"))
    }
   p.future
}

I'd rather use your second approach.

like image 187
Pablo Francisco Pérez Hidalgo Avatar answered Oct 23 '22 19:10

Pablo Francisco Pérez Hidalgo