I'm new to Scala, being slightly confused at the various ways to handle exceptions and looking for best-practice advice on the topic. I'm writing a simple method to retrieve a Customer using an existing blocking SDK. The possible outcomes are:
Customer is found
Customer is not found (comes back as a NotFoundException from the SDK)
Error while talking to remote server (SDK throws some other exception)
So I want my method to have a return type Future[Option[Customer]], and return for each case above:
Successful Future: Some(customer)
Successful Future: None
Failed Future
Here is what I wrote using try/catch:
private def findCustomer(userId: Long): Future[Option[Customer]] = future {
try {
Some(gateway.find(userId))
} catch {
case _: NotFoundException => None
}
}
This works fine and seems clean to me, but doesn't seem to be really the "Scala way" to do it - I've been told to avoid using try/catch. So I've been looking for a way to rewrite it using a Try instead.
Here are 2 variants that (I think) behave exactly the same, using a Try.
Variant A:
private def findCustomer(userId: Long): Future[Option[Customer]] = future {
Try(
Some(gateway.find(userId))
).recover {
case _: NotFoundException => None
}.get
}
Variant B:
private def findCustomer(userId: Long): Future[Option[Customer]] = future {
Try(
Some(gateway.find(userId))
).recover {
case _: NotFoundException => None
}
} flatMap {
case Success(s) => Future.successful(s)
case Failure(f) => Future.failed(f)
}
I'm not a huge fan of A (even though it's more concise than B) since the .get seems a bit treacherous. B is definitely the most explicit but mapping the Try cases to corresponding Future outcomes seems boring.
How would an experienced Scala programmer write this?
I think your initial version using try/catch is perfectly fine, because it's a wrapper around an existing SDK.
Alternatively, you can use the recover method on Future:
def findCustomer(userId: Long): Future[Option[Customer]] =
Future(Option(gateway.find(userId))).recover {
case e: NotFoundException => None
}
One option is to avoid chaining Try and Future. In a sense, a future is an asynchronous Try.
You could use Future[Customer] directly and consider NotFoundException as something to recover from rather than a None value. Normally, you would chain operation on the future itself, with no need to deal with the failure cases (by mapping it, etc). It all depends on how you are going to use it (how your logic branches when the future is completed). In other words, maybe it seems convoluted to you because it is, and you are forcing it to be by forcing the Future[Option[Customer]] return type.
The ability to do chain multiple operations and carry on with computing if and only if all if going all right is the nice Scala feature here (especially on for comprehensions, etc).
You might be looking for:
Future.fromTry(myTry)
So for your example you might do:
Future.fromTry {
Try(Some(gateway.find(userId))).recover {
case _: NotFoundException => None
}
}
To only catch the NotFoundException, as in your solution. The only problem here is that the method won't be executed asynchronously. If that is necessary, consider using Future.recover, as suggested by Ionut.
Another idiomatic option worth mentioning here is to use Either[F, S], where S is the type of the successful return, and F can hold the errors (which you might want to propagate). So you could use Either[ Exception, Option[String]] or Either[String, Option[String]] where the first String is an error message.
If you're ready to adopt some scalaz. disjunction from scalaz is very useful and natural in handling erronous scenarios. It's like scala Either but scalaz disjunction \/ is right biased. you'll have success value on the right and exception on left. wrapping your code block with \/.fromTryCatch returns exception on the left side of disjunction. right will always have success value. mapping over disjunction is easiser than Scala Either since disjunction is right biased and easily gives you value from right.
import scalaz.\/
private def findCustomer(userId: Long): Future[\/[Throwable,Option[Customer]] ]= future {
\/.fromTryCatch {
Some(gateway.find(userId))
}
}
Related
I am trying to understand the difference why someone will use Future.successful(Failure(ServiceException("error"))) rather than Future.failed(ex) in Scala.
Future.successful(
Failure(
ServiceException(ErrorTypes.Validation, "Value should not be more than 30")
)
)
It's hard to say why the author did what they did, I can guess a couple of reasons:
They are not aware that you can Future.fail to return a failed future. This may happen because the signature of future doesn't convey the failure path explicitly in the type. When you see Future[A], you may think this computation must succeed in the future
They wanted to be explicit about failure. When I declare Future[Try[A]], I am forcing the caller to deal with the fact that can't directly access A, but have to "jump another hop" to get inside for A. Although this is delicate, as one can easily write:
val f: Future[Try[String]] = ???
f.map(_.map(str => // stuff with string))
But perhaps the signature forces the caller to realize the possibility of failure. Although Try is pretty limiting in it's expressiveness as well, as it can only return a Throwable on the failure path. I find Future[Either[E, A]] to be more expressive in that regards, which allows you to build your own failure hierarchy and encode it in the type.
There is an ever lasting debate regarding unary functor IO types (Future[A]) vs bi-functor IO types (IO[E, A], i.e. ZIO). I am in favor of bi-functor and have been using it solely recently, and I find it to be really time-saving when I don't have to dig into the implementation to understand the possible failures of a computation.
I would like to add on top of the great answer by #YuvalItzchakov, that writing code as in your question complicates the code and makes it unnatural. For example, let's assume that this future normally an Int, something like:
val succeeded = true // or any other computation
val future = if(succeeded) {
Future.successful(42)
} else {
Future.successful(Failure(new Exception("some failure text")))
}
Then first, the type of future is Future[Any], because Any is the lowest common root of Int and Failure, which is not convenient. But then, to map on it, you need to do:
future.map {
case i: Int =>
println(i)
case Failure(ex) =>
println(ex.getMessage)
} recover {
case NonFatal(ex) =>
println(s"recover ${ex.getMessage}")
}
Which feels weird to pattern match Int and Failure in the same match case. If you do want to pattern match Success and Failure, we had to do:
val succeeded = true // or any other computation
val future = if(succeeded) {
Future.successful(Success(42))
} else {
Future.successful(Failure(new Exception("some failure text")))
}
But that is already pretty clear as redundancy, right? No one will call Future.successful(Success(42)). So I think that it is pretty clear to use Future.failed(new Exception("some failure text")).
For the sake of simplicity suppose we have a method called listTail which is defined in the following way:
private def listTail(ls: List[Int]): List[Int] = {
ls.tail
}
Also we have a method which handles the exception when the list is empty.
private def handleEmptyList(ls: List[Int]): List[Int] = {
if(ls.isEmpty) List.empty[Int]
}
Now I want to create a safe version of the listTail method, which uses both methods:
import scala.util.{Try, Success, Failure}
def safeListTail(ls: List[Int]): List[Int] = {
val tryTail: Try[List[Int]] = Try(listTail(ls))
tryTail match {
case Success(list) => list
case Failure(_) => handleEmptyList(ls)
}
}
My question is, if the two private methods are already tested, then should I test the safe method as well? And if yes, how?
I was thinking just to check if the pattern matching cases are executed depending on the input. That is, when we hit the Failure case then the handleEmptyList method is executed. But I am now aware of how to check this.
Or do I need to refactor my code, and put everything in a single method? Even though maybe my private methods are much more complex than this in the example.
My test are written using ScalaTest.
Allowing your methods to throw intentionally is a bad idea and definitely isn't in the spirit of FP. It's probably better to capture failure in the type signature of methods which have the ability to fail.
private def listTail(ls: List[Int]): Try[List[Int]] = Try {
ls.tail
}
Now your users know that this will return either an Success or a Failure and there's no magic stack unrolling. This already makes it easier to test that method.
You can also get rid of the pattern matching with a simple def safeTailList(ls: List[Int]) = listTail(l).getOrElse(Nil) with this formulation -- pretty nice!
If you want to test this, you can make it package private and test it accordingly.
The better idea would be to reconsider your algorithm. There's machinery that makes getting the safe tail built-in:
def safeTailList(ls: List[Int]) = ls.drop(1)
It is actually the other way around: normally, you don't want to test private methods, only the public ones, because they are the ones that define your interactions with the outside world, as long as they work as promised, who cares what your private methods do, that's just implementation detail.
So, the bottom line is - just test your safeListTail, and that's it, no need to test the inner implementation separately.
BTW, you don't need the match there: Try(listTail(ls)).getOrElse(handleEmptyList(ls)) is equivalent to what you have there ... which is actually not a very good idea, because it swallows other exceptions, not just the one that is thrown when the list is empty, a better approach would be actually to reinstate match but get rid of Try:
ls match {
case Nil => handleEmptyList(ls)
case _ => listTail(ls)
}
What is the idiomatic way in Scala to type an API that may or may not return results?
I can simply type to Future[Option[A]] and return a None if there is no results, but this will make things a little more verbose for API clients I think because it has to do some pattern matching (or other techniques like folding) over the Option returned.
trait SomeAPI {
def fetch(): Future[Option[String]]
}
object APIImpl {
def asyncDBCall(): Future[List[String]] = ???
override def fetch(): Future[Option[String]] = asyncDBCall().map(r => if (r.isEmpty) None else Some(r.head)
}
However, Futures already give us a way to say that there are no results making it fail. But I don't know if this is a proper or expressive way of say that the call doesn't return results.
trait SomeAPI {
def fetch(): Future[String]
}
class NoResultsException extends Exception
object APIImpl extends SomeAPI {
def asyncDBCall(): Future[List[String]] = ???
override def fetch(): Future[String] = asyncDBCall().map(r => if (r.isEmpty) throw new NoResultsException() else r.head)
}
At first it seems that both options are acceptable and the final decision may be just about personal preference, but maybe I'm missing something and make a Future fail is not a good option.
Exceptions should be reserved for errors. If the API may or may not returns results, and both cases are considered normal, then the API should use Option.
If that makes it more verbose by forcing the client to handle both situations, that's good, because the client should handle both situations. Exceptions are good when you don't want to force the client code to handle an exceptional case (unexpected error) which should probably cascade and be handled by an error handler.
Now, if lack of results truly indicates that something went wrong, then an exception would be appropriate.
I'd expect clients want to treat "success, no results" and failure differently, generally speaking (e.g. retry in the second case, but not the first). If you denote the first with a failed future, you make verbosity for the clients worse, not better: they just need to check the exception instead of the success, and the API isn't helping them to be aware of that like Future[Option[A]] would.
If you really worry about verbosity, you could add custom extractors or methods for the clients to use if they want, e.g.
implicit class FutureOption[A](future: Future[Option[A]]) {
def mapOpt[B](handleSuccessWithResult: A => B, handleSuccessWithNoResult: () => B) = Future.map {
case Some(x) => onSuccessWithResult(x)
case None => onSuccessWithNoResult()
}
}
The others are right. Failures are not a good way to describe the lack of a concrete result, if this is part of regular operation.
Scala gives you another possibility to explicitly note, that there may be no results, sealed traits.
sealed trait MyOperationReturn
case object OkNoResult
case class OkWithStringResponse(data: String) extends MyOperationReturn
case class OkWithIntResponse(data: Int) extends MyOperationReturn
The user can then just pattern match. This option is especially handy, if you have either multiple return types (not only two) and no natural empty return type (like Seq.empty)
I have the below block of code. My intent is to do some processing with the method params and return Future[Int] or fail the Future. I want to fail the Future if my argument to method inside the Future is of type None. I have the below code. I wonder if there is a much more concise and functional way to achieve the same.
def testoption(myFutOptInt: Future[Option[Int]]) = {
myFutOptInt.map { myOptInt =>
myOptInt match {
case None => throw new BadRequestException("Oh no.....") //Future.failed(BadRequestException(""))
case Some(myInt) => myInt + 45
}
} recoverWith {
case e:Exception => Future.failed(e)
}
}
I am not liking the part that I have to do a match, wondering if there is a way to do a map or something better and still achieve it. If I do getOrElse I guess I won't be able to write the business logic if the value is not None? So getOrElse is not possible either I think?
The other thing I do not like about this code is that I am throwing an exception and then I have to do recoverWith to fail the Future.
I can enhance the above code how?
If you need specifically BadRequestException, this is enough:
def testoption(myFutOptInt: Future[Option[Int]]) =
myFutOptInt.map {
case None => throw new BadRequestException("Oh no.....")
case Some(myInt) => myInt + 45
}
And if you don't care about particular error type, you can cut it even more:
def testoption(myFutOptInt: Future[Option[Int]]) = myFutOptInt.map(_.get + 45)
This will give you java.util.NoSuchElementException: None.get if there's no value
recoverWith is absolutely redundant b/c if an exception is thrown inside a map, whole operation results in a failed Future. This is true for most, if not all, Future methods.
For match, you can avoid it using opt.fold(throw ...)(...) or .map(...).getOrElse(throw ...), but I'd argue those options are less readable. I suggest just using pattern-matching function definition to reduce extra code
Given an Option, what is the idiomatic way to get its value or throw an exception trying?
def foo() : String = {
val x : Option[String] = ...
x.getOrException()
}
A throw "statement" is really an expression in Scala, and it has type Nothing, which is a subtype of every other type. This means you can just use plain old getOrElse:
def myGet[A](oa: Option[A]) = oa.getOrElse(throw new RuntimeException("Can't."))
You really, really shouldn't be doing this, though.
(EDIT: this is not the best or most idiomatic way to do it. I wrote it when I was not familiar with Scala. I leave it here for an example of how not to do it. Nowadays I would do as #TravisBrown)
I think it really boils down to two things:
how sure are you that the value is there?
how do you want to react if it isn't?
If at that point in your code you expect the value to be there, and in the remote case that it isn't you want your program to fail fast, then I would only do a normal get and let Scala throw a NoSuchElementException if there was no value:
def foo() : String = {
val x : Option[String] = ...
x.get
}
If you want to handle the case differently (throw your own exception) I think a more elegant way would look like this:
def foo(): String = {
val x: Option[String] = None
x match {
case Some(value) => value
case None => throw new MyRuntimeException("blah")
}
}
And of course if you want to supply your own alternative value for the case that the Option is None you would just use getOrElse:
def foo(): String = {
val x: Option[String] = None
x.getOrElse("my alternative value")
}
I hope this will help you to understand how to represent errors (and generally effects) using types.
Error handling strategies in functional Scala
Use Option to return optional values. For example - fail to find entity in storage.
Use Option(possiblyNull) to avoid instances of Some(null).
Use Either[Error, T] to report expected failure. For example - email format is wrong, cannot parse a string to a number, etc.
Model your errors as ADTs (simply speaking kind of type hierarchies) to use it, for example, on the Left of the Either to represent more complex error scenarios.
Throw Exception only to signal unexpected and not-recoverable failures. Like missing config file.
Use Either.catchOnly or Try or Cats.IO (advanced) rather than a catch block for handling unexpected failures. Hint: You can still use ADT but extend them from throwables. More about Either vs Try.
Use Validated data-type from Cats lib to accumulate errors rather than fail-fast (Either), but prefer Either's on module-level to simplify the composition of the program (to have the same types). For example - form data validation, parsing errors accumulation.
Use mentioned types and don't optimize program preemptively - since most probably, bottle-necks would be in business logic, not in effect types.
Such an approach will simplify maintenance and updates of your code since you can reason about it without going to implementation specifics (aka local-reasoning). Also - reduce bugs - you cannot miss an error in the type. And compose the program easier (with help of map, flatMap and other combinators) - since it's simpler on type level, rather than with non-local exceptions and side-effects.
More about learning functional Scala.
But be aware that sometimes with this approach types could stack up and it could become harder to compose things. Given, for example: x: Future[Either[Error, Option[T]]] What you can do:
Use map and flatMap in combination with pattern-matching to compose different values of such types, for example:
x.faltMap { case Right(Some(v)) => anotherFuture(v); case Left(er) => ... }
If it doesn't help you can try to use MonadTransformers (don't be scared of the name, it's just wrappers around the effect types like Either and Future)
Also, an option is to simplify your errors ADT by extending them from the Throwable to unify it with Future, then it'll be Future[Option[T]]
And finally, in your case one option will be:
def foo() : Either[Error, String] = {
val x : Option[String] = ...
x match {
case Some(v) => Right(v)
case None => Left(Error(reason))
}
}
Just use the .get method.
def get[T](o:Option[T]) = o.get
It will throw a NoSuchElementException if o is an instance of None.
Basically, I would work with options like this:
def addPrint(oi:Option[Int]) = oi.map(_+1).foreach(println)
addPrint(Some(41))
addPrint(Some(1336))
addPrint(None)
to avoid your specific question.
Scala now support this operation on maps using getOrElse() method, see documentation here
As pointed out already, throwing an exception in Scala is an expression as well.
So you can do the following:
myMap.getOrElse(myKey, throw new MyCustomException("Custom Message HERE")