Using Try out of Exception contexts - scala

Is it in Scala acceptable to use Try (Success/Failure) outside of an actual Exception context as a return type of a function that can succeed or fail?

It is certainly possible to use a Try outside an exception context; I use it that way all the time. That does not necessarily mean that it is "acceptable" :)
I would say that the whole point of using Try is to take the Throwable instance out of the exception context and put it in an object that can be used anywhere in a program. It is hard to see why Try would have such a rich set of methods (e.g. flatMap) if it is only intended to be used inside an exception context.

Instances of Try, are either Success or Failure, where Failure is
case class Failure[+T](exception: Throwable)
Note how Failure must be constructed with Throwable, so I think Try is meant to be used within context of Throwables. So we cannot do something like
def foo: Try[Int] = {
Failure(42) // Error: type mismatch; found : Int(42) required: Throwable
}
Consider using Either instead of Try outside exceptions context.
Addressing the comment consider
Valid/Invalid from cats: https://typelevel.org/cats/datatypes/validated.html
define your own ADT with your own meaning of success and failure cases, and then wrap function results in those cases
In neither of these are you forced to use exceptions.
Here is an example
sealed trait MyValidationADT[T]
case class Good[T](result: T) extends MyValidationADT[T]
case class Bad[T](result: T) extends MyValidationADT[T]
def foo(i: Int): MyValidationADT[Int] = Bad(42)
foo(11) match {
case Good(result) => "woohoo"
case Bad(result) => "boom"
}
which outputs
res0: String = boom

Related

Service class and logging design Scala with Futures

I have a service class that gets some data from a database (for context, I'm using Play! Framework). Here's an example method:
def getAccessToken(id: BSONObjectID): Future[Option[String]] = {
userDAO.find(id).map {
case Some(user) =>
user.settings flatMap (_.accessToken)
case _ => None
}
}
I'm trying to improve the error handling (new to Scala) on this thing as there are a couple things that can go wrong:
User might not be found
User might be found but accessToken might not be set (accessToken is an Option[String])
As it stands right now, I can't differentiate between the two. My natural inclination to fix this is to use \/ from Scalaz and have the return type be Future[ErrorType \/ String] and this seems like a reasonable approach. In my controller method, I can for comprehension a bunch of different service methods like this by lifting into a wrapper monad.
But I have the following questions:
Should my ErrorType extend Exception, or should I just use the sealed trait style and just extend from that. I've heard that it is not good practice to use exceptions in Scala, so I'm not sure what the right approach is.
How can I handle logging without polluting the controller class with excessive log statements? If a controller class calls a bunch of these service methods, the controller will have to handle several different ErrorTypes in the for comprehension. Assuming I'm lifting all the monads to a wrapper monad with ?|, I want to avoid this:
accessToken <- service.getAccessToken(id) ?| { error => error match { case Error1 =>
logger.error(
"Unable to find access token for user: " + id
.toString())
InternalServerError(
ApiResponse("internal_server_error",
"Unable to get token."))
case Error2 => ...
}
Thanks!
I think Future[ErrorType / String] is a bit overkill as Future[T] already can hold either an object of type T or an Exception derived one (see Future.successful(...)/ Future.failed(...))
Should my ErrorType extend Exception, or should I just use the sealed trait style and just extend from that. I've heard that it is not good practice to use exceptions in Scala, so I'm not sure what the right approach is.
I would recommend to use a class (or a set of classes, one per particular error type), say YourAppException derived from Exception, as you need to handle low level exceptions one way or another anyway.
I agree that throwing/catching exceptions is not going very well with functional code and it is better to use Try[T] or Future[T] to return errors in a more explicit way instead. On the other hand there is nothing wrong with using an Exception derived class to hold some error info. It is often useful to wrap the original non-application (say IO) exception in an application one and keep a reference to the initial one in Exception's 'cause' for troubleshooting. It gives an opportunity to provide a more context specific error message.
How can I handle logging without polluting the controller class with excessive log statements?
Consider encapsulating error messages in Exception derived case classes, representing application errors so you can access the error messsages uniformly using exception.getMessage. It is easy to add some method to YourAppException to construct ApiResponse as well.
def getAccessToken(id: BSONObjectID): Future[String] = {
userDAO.find(id).flatMap {
case Some(user) =>
val optToken = user.settings.flatMap (_.accessToken)
optToken.map(Future.successful).getOrElse(Future.failed(AccessTokenIsInvalid(user)))
case _ => Future.failed(UserNotFoundError(user))
}
}
case class AccessTokenIsInvalid(user: String)
extends YourAppException(s"Access token is invalid for user $user") {
}
accessToken <- service.getAccessToken(id) ?| { error =>
logger.error(error.getMessage)
InternalServerError(
ApiResponse("internal_server_error", error.getMessage))
}
1) Yes, you are on a right way. The problem with exceptions is that when something fails it's hard to pattern-match on it to detect a reason.
I would do it like that:
sealed trait MyError
object UserNotFound extends MyError
object AuthFailed extends MyError
type MyResult = Either[MyError, String]
2) If a program is well-typed logging is necessary in places where information is lost.
If you deal with, for example, val x = Future[Either[Error, String]], then you haven't throttled potential error yet so logging is optional.
But when you somehow try to extract Either[MyError, String] from it you lose information so you should log it.
The same happens when you extract String from Either[MyError, String].

Scala Multiple Future wrapped in Try

Suppose I have many akka services which all return a case class of type AbcDto wrapped in a Try.
So I call all these services using map and get back a List[Future[Any]].
Now I use Future.sequence to convert this to Future[List[Any]].
How do I unwrap my final list of results? I want to process them only when all of them are a Success and even if one fails I want to throw an error.
I tried mapping Future[List[Any]] as:
val a: List[Future[Any]]
a.map {
case r: List[Success[AbcDto]] => println("hello")
}
But this gives error:
case r: List[Try[AbcDto]]. At this point its giving error: non-variable type argument scala.util.Try[AbcDto] in type pattern List[scala.util.Try[AbcDto]] (the underlying of List[scala.util.Try[AbcDto]])
since all akka services return AbcDtowrapped in a Try the proper type of val a should be List[Future[Try[AbcDto]]]. Now the desired result can be achieved by a combination of Future.sequence and flatMap operation to check for any Failures in the service as shown below.
val a: List[Future[Try[AbcDto]]] = ...
val result: Future[List[AbcDto]] = Future.sequence(a) flatMap {
case r: List[Try[AbcDto]] #unchecked if r.find(!_.isSuccess).isDefined => Future.failed(new RuntimeException("not all results are successful"))
case r => Future.successful(r.collect({ case Success(x) => x}))
}
Future[A] and Try[A] in the actor context are so similar that I see no point to return Try[A] from those actors. You just return A in case of success, which will be Future[A] on asking side, a List of which you can sequence and get Future[List[A]], which, in case of a single failure, will contain the first encountered exception. It seems to be exactly what you're asking for.
To communicate failure from an actor to the asker, you should send akka.actor.Status.Failure with the relevant instance of Throwable.
P.S. regarding comment that using try-catch is non-idiomatic Scala. It actually is. Here's how Try creation is implemented:
object Try {
/** Constructs a `Try` using the by-name parameter. This
* method will ensure any non-fatal exception is caught and a
* `Failure` object is returned.
*/
def apply[T](r: => T): Try[T] =
try Success(r) catch {
case NonFatal(e) => Failure(e)
}
}
As you can see, it uses try-catch inside. If Scala standard library authors are fine with that, so should be you. :)
If I understand you correctly (the types in your question are a bit confusing),
You start with a val responseFutures: List[Future[Any]] and after conversion you have a val responsesFuture: Future[List[Any]]. rogue-ones answer is correct, but it could use some clarification:
Your compiler error is caused by the fact that Success is not a class, but an extractor object with unapply for Try. Therefore you cannot use in in type extraction this way.
So something like case r: List[Try[AbcDto]] if r.forall(_.isSuccess) => println("hello") should compile. However, as AbcDto is erased, you will get a compiler warning about erasure. Thus the #unchecked.
UPDATE
Type erasure means, the compiler cannot check type argument type in pattern matches at compile time. In your case, all the compile knows of your input type is Future[List[Try[Any]]].
So
future.map {
case _: List[Try[AbcDto]] => ???
}
will cause a compiler warning because the compiler only sees.
future.map {
case _: List[Try[_]] => ???
}
The #unchecked annotation just suppresses the corresponding compiler warning.
In the end with the pattern match above you just cast whatever is coming in to Try[AbcDto] without any compile time type safety.

Scala type signature failing with subclasses

I have the following exception hierarchy defined:
/**
* Base class for all exceptions in this library
*/
trait MyAkkaHttpException {}
/**
* Thrown when there is a problem persisting data to a datastore
*/
case class PersistenceException(message: String)
extends Exception(message: String) with MyAkkaHttpException
/**
* Thrown when validation on an object fails
* #param errors
*/
case class ValidationException(message: String, errors: List[String])
extends Exception(message: String) with MyAkkaHttpException
And the following code:
class ContactFormService(contactFormPersistor: ContactFormPersistor) {
def handleForm(contactForm: ContactForm): ValidationNel[MyAkkaHttpException, String] = {
contactForm.validate() match {
case Success(_) => contactFormPersistor.persist(contactForm)
case Failure(e) =>
new ValidationException(message = "Error validating contact form",
errors = e.toList).failureNel[String]
}
}
}
contactFormPersistor.persist returns ValidationNel[PersistenceException, String]
contactForm.validate() returns ValidationNel[String, Boolean]
The problem is handleForm won't accept that PersistenceException and ValidationException are subclasses of MyAkkaHttpException. What do I need to do to make it correctly realise that those return types are valid subclasses?
Try changing ValidationNel[MyAkkaHttpException, String] to Validation[NonEmptyList[MyAkkaHttpException], String]. As someone pointed out in the comments, it's only the type alias that is not covariant in the first type argument.
type ValidationNel[E, +X] = Validation[NonEmptyList[E], X]
Otherwise, NonEmptyList and Validation are both covariant in all their arguments.
EDIT:
This might depend on your version of scalaz. As far as the latest available that I can browse, it looks like ValidationNel is no longer covariant in both arguments, but it previously was. There is probably a good reason for this change: be prepared to not be able to use Scalaz's functions for ValidationNel.
Either is covariant over both the left and right, so I've just switched to that instead.
The problem is you need covariance on the first type parameter of ValidationNel and this particular shortcut of Validation was not designed with this covariance in mind*
Based on the information i gathered from our comment exchange, i believe this is the correct way forward. Declare your own alias (or use the type directly)
type MyValidationNel[+E, +X] = Validation[NonEmptyList[E], X]
*) I do however have a feeling there was a reason behind not having covariance on the E param (as scalaz normally is know to do things with a reason)

Does it make sense to return Try[Option[String]]?

I'm writing a method which has three possible return states:
The operation can have failed with an exception
The operation can have succeeded with a String result
The operation can have succeeded with no results
Does the return type Try[Option[String]] seem correct? Or is there a better alternative?
Lift's Box does a nice job of combining the semantics of Option and Try
A Box can be:
Full(value) => Some(value): Option
Empty => None: Option
Failure(ex) => Failure: Try
http://scala-tools.org/mvnsites/liftweb-2.3/net/liftweb/common/Box.html
Your approach is perfectly fine. Another approach is to define your own case classes/objects
sealed trait Result
case class OperationFailed(e: Exception) extends Result
case class SuccessValue(str: String) extends Result
case object SuccessNoValue extends Result
Edit:
This approach may work better with pattern matching, specially if you are using Akka.
val f: Any => Any = { case x: Try[Option[String]] => x.get.get.toUpperCase }
f(Try(Some("x")))
f(Try(Some(5))) // Throws java.lang.ClassCastException
The above would show the following warning
warning: non-variable type argument Option[String] in type pattern
scala.util.Try[Option[String]] is unchecked since it is eliminated by
erasure

What are the use cases for Scala 2.9's try...catch generalization?

I've read about and experimented with the Scala 2.9 try...catch feature, and it has me thinking about possibilities. What would I actually use it for other than saving a couple of lines of code?
Scala 2.9 Final Release Notes
The use case is to be able to have generic error handling throughout your application. Let's say you want to handle all FileNotFoundExceptions in your application by sending an e-mail to an administrator. Previously, you'd have to do it like this:
// Globally
val fileNotFound: PartialFunction[Throwable, Unit] = {
case e: FileNotFoundException =>
// Create report and send the e-mail
}
// On each try-catch-block
try {
// Open file
}
catch {
case fnf: FileNotFoundException => fileNotFound(fnf)
}
Now you just do:
try {
// Open file
} catch fileNotFound
This also has the nice advantage that you can link several such exception handlers using the orElse method on partial functions:
val fileErrors = fileNotFound orElse endOfFile orElse invalidFormat
And then just use that everywhere where you need file exception handling. Such an error handler can be dynamically combined based on the configuration file for the application, for example. This is much less cumbersome than pattern matching everywhere and calling the correct handler.
One useful thing which could be pimped on top of partial functions is the andAlso operator, which acts as a sequencing operator on two partial functions. This would be useful when you want to do some error handling specific to a particular try-catch block after having done the generic error handling.
implicit def pf2ops(pf: PartialFunction[Throwable, Unit]) = new {
def andAlso(localpf: PartialFunction[Throwable, Unit]) = new PartialFunction[Throwable, Unit] {
def apply(t: Throwable) = {
if (pf.isDefinedAt(t)) pf(t)
localpf(t)
}
def isDefinedAt(t: Throwable) = pf.isDefinedAt(t) || localpf.isDefinedAt(t)
}
}
And then you can do this:
scala> try {
| throw new java.io.FileNotFoundException
| } catch fnf andAlso {
| case e: Exception => println("I don't know, but something is specific to this particular block.")
| }
I don't know, but something is specific to this particular block.
I guess you could play further with the exact semantics and the meaning (and the name) of andAlso.
Good answer by axel22, but I think the real reason for its introduction is something else. The try/catch/finally handling introduced a special case. You used a partial function literal, but you could not actually replace that with a partial function. Now, catch just receive a partial function, and one more special case in the language is gone.