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")).
Related
This might be a really dumb question but I am trying to understand the logic behind using #flatMap and not just #map in this method definition in Finatra's HttpClient definition:
def executeJson[T: Manifest](request: Request, expectedStatus: Status = Status.Ok): Future[T] = {
execute(request) flatMap { httpResponse =>
if (httpResponse.status != expectedStatus) {
Future.exception(new HttpClientException(httpResponse.status, httpResponse.contentString))
} else {
Future(parseMessageBody[T](httpResponse, mapper.reader[T]))
.transformException { e =>
new HttpClientException(httpResponse.status, s"${e.getClass.getName} - ${e.getMessage}")
}
}
}
}
Why create a new Future when I can just use #map and instead have something like:
execute(request) map { httpResponse =>
if (httpResponse.status != expectedStatus) {
throw new HttpClientException(httpResponse.status, httpResponse.contentString)
} else {
try {
FinatraObjectMapper.parseResponseBody[T](httpResponse, mapper.reader[T])
} catch {
case e => throw new HttpClientException(httpResponse.status, s"${e.getClass.getName} - ${e.getMessage}")
}
}
}
Would this be purely a stylistic difference and using Future.exception is just better style in this case, whereas throwing almost looks like a side-effect (in reality it's not, as it doesn't exit the context of a Future) or is there something more behind it, such as order of execution and such?
Tl;dr:
What's the difference between throwing within a Future vs returning a Future.exception?
From a theoretical point of view, if we take away the exceptions part (they cannot be reasoned about using category theory anyway), then those two operations are completely identical as long as your construct of choice (in your case Twitter Future) forms a valid monad.
I don't want to go into length over these concepts, so I'm just going to present the laws directly (using Scala Future):
import scala.concurrent.ExecutionContext.Implicits.global
// Functor identity law
Future(42).map(x => x) == Future(42)
// Monad left-identity law
val f = (x: Int) => Future(x)
Future(42).flatMap(f) == f(42)
// combining those two, since every Monad is also a Functor, we get:
Future(42).map(x => x) == Future(42).flatMap(x => Future(x))
// and if we now generalise identity into any function:
Future(42).map(x => x + 20) == Future(42).flatMap(x => Future(x + 20))
So yes, as you already hinted, those two approaches are identical.
However, there are three comments that I have on this, given that we are including exceptions into the mix:
Be careful - when it comes to throwing exceptions, Scala Future (probably Twitter too) violates the left-identity law on purpose, in order to trade it off for some extra safety.
Example:
import scala.concurrent.ExecutionContext.Implicits.global
def sneakyFuture = {
throw new Exception("boom!")
Future(42)
}
val f1 = Future(42).flatMap(_ => sneakyFuture)
// Future(Failure(java.lang.Exception: boom!))
val f2 = sneakyFuture
// Exception in thread "main" java.lang.Exception: boom!
As #randbw mentioned, throwing exceptions is not idiomatic to FP and it violates principles such as purity of functions and referential transparency of values.
Scala and Twitter Future make it easy for you to just throw an exception - as long as it happens in a Future context, exception will not bubble up, but instead cause that Future to fail. However, that doesn't mean that literally throwing them around in your code should be permitted, because it ruins the structure of your programs (similarly to how GOTO statements do it, or break statements in loops, etc.).
Preferred practice is to always evaluate every code path into a value instead of throwing bombs around, which is why it's better to flatMap into a (failed) Future than to map into some code that throws a bomb.
Keep in mind referential transparency.
If you use map instead of flatMap and someone takes the code from the map and extracts it out into a function, then you're safer if this function returns a Future, otherwise someone might run it outside of Future context.
Example:
import scala.concurrent.ExecutionContext.Implicits.global
Future(42).map(x => {
// this should be done inside a Future
x + 1
})
This is fine. But after completely valid refactoring (which utilizes the rule of referential transparency), your codfe becomes this:
def f(x: Int) = {
// this should be done inside a Future
x + 1
}
Future(42).map(x => f(x))
And you will run into problems if someone calls f directly. It's much safer to wrap the code into a Future and flatMap on it.
Of course, you could argue that even when using flatMap someone could rip out the f from .flatMap(x => Future(f(x)), but it's not that likely. On the other hand, simply extracting the response processing logic into a separate function fits perfectly with the functional programming's idea of composing small functions into bigger ones, and it's likely to happen.
From my understanding of FP, exceptions are not thrown. This would be, as you said, a side-effect. Exceptions are instead values that are handled at some point in the execution of the program.
Cats (and i'm sure other libraries, too) employs this technique too (https://github.com/typelevel/cats/blob/master/core/src/main/scala/cats/ApplicativeError.scala).
Therefore, the flatMap call allows the exception to be contained within a satisfied Future here and handled at a later point in the program's execution where other exception value handling may also occur.
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
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))
}
}
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")