Should my custom exception types be case classes?
On the plus side, I get extractors.
On the minus side, I get incorrect equality semantics. But I can avoid that by overriding equals.
So does it make sense, at a conceptual level, to make them case classes?
This is very subjective of course, but in my opinion it is good practice to have exception classes as case classes.
The main rationale being that when you catch an exception, you are doing pattern matching, and case classes are much nicer to use in pattern matching.
Here's an example that takes advantage of the ability to use the full power of pattern matching in a catch block, when using a case class exception:
object IOErrorType extends Enumeration {
val FileNotFound, DeviceError, LockedFile = Value
}
case class IOError(message: String, errorType: IOErrorType.Value) extends Exception(message)
def doSomeIO() { throw IOError("Oops, file not found!", IOErrorType.FileNotFound) }
try {
doSomeIO()
} catch {
case IOError( msg, IOErrorType.FileNotFound ) =>
println("File not found, please check the path! (" + msg + ")")
}
In this example, we have only one exception, but it contains an errorType field for when you want to know the exact error type that occurred (usually this is modelled through a hierarchy of exception, I'm not saying this is better or worse, the example is just illustrative). Because the IOError is a case class I can simply do case IOError( msg, IOErrorType.FileNotFound ) to catch the exception only for the error type IOErrorType.FileNotFound. Without the extractors that we get for free with case classes, I would have to catch the exception everytime, and then rethrow in case I'm actually not interested, which is definitely more verbose.
You say that case classes give you incorrect equality semantics. I don't think so. You, as the writer of the exception class gets to decides what equality semantics makes sense. After all when you catch an exception, the catch block is where you decide which exceptions to catch usually based on the type alone, but could be based on the value of its fields or whatever, as in my example. The point is that the equality semantics of the exception class has little to do with that.
One common idiom you lose by making exceptions case classes is the pattern of creating a subclass hierarchy of exceptions with subclassing used to indicate greater specificity of the error condition. Case classes can't be subclassed.
I like the answer from RĂ©gis Jean-Gilles. But if you have a good reason to not make a case class (see answer of Dave Griffith), you can archieve the same as in the sample above with a normal class and unapply:
object IOErrorType extends Enumeration {
val FileNotFound, DeviceError, LockedFile = Value
}
object IOError {
def unapply(err: IOError): Option[(String, IOErrorType.Value)] = Some(err.message, err.errorType)
}
class IOError(val message: String, val errorType: IOErrorType.Value) extends Exception(message)
def doSomeIO() { throw new IOError("Oops, file not found!", IOErrorType.FileNotFound) }
try {
doSomeIO()
} catch {
case IOError( msg, IOErrorType.FileNotFound ) =>
println("File not found, please check the path! (" + msg + ")")
}
Related
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
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].
I'm overriding a method in spray-json in order to catch an exception it throws. In one particular case I want to add special handling, but otherwise I want to rethrow the original exception.
But the exception isn't rethrowing.
object MyObject extends DefaultJsonProtocol {
override def fromField[T](value: JsValue, fieldName: String)(implicit reader: JsonReader[T]) = {
try {
super.fromField(value, fieldName)(reader)
} catch {
case e: DeserializationException =>
if (reader.isInstanceOf[SafeListFormat[_]]) EmptyList.asInstanceOf[T]
else deserializationError("Object is missing required member '" + fieldName + "'", e)
}
}
}
The deserializationError in spray-json: https://github.com/spray/spray-json/blob/master/src/main/scala/spray/json/package.scala
The method I'm overriding is fromField in here: https://github.com/spray/spray-json/blob/master/src/main/scala/spray/json/ProductFormats.scala
In debug mode I can see the original exception being caught, and my else case "rethrowing" the error, and even the deserializationError method being called in spray-json's library. But even through the DeserializationException is a RuntimeException the overridden method doesn't terminate.
I had wondered if there was something about deferred execution of the deserializationError method. But I even tried changing my exception handler from calling deserializationError to a simple throw e and that didnt work either.
What's going on here?
PS. I dont think this is specific to spray-json really, but is probably something interesting I dont know about scala. But I kept the example as a real world as possible just in case it is something particular to the way my overridden method is called by the spray-json library code.
EDIT: Raised same question with spray-user forum: https://groups.google.com/forum/#!topic/spray-user/YXtY6VyIVGk
It seem ok to me to be honest. So here go a few random observations (too long to put as comments):
Call super.fromField[T], right now you are not passing the T param. When things are weird, try to make everything explicit.
You can try functional error handling:
Try(super.fromField(value, fieldName)(reader)).recover {
case e: DeserializationException if (reader.isInstanceOf[SafeListFormat[_]]) => EmptyList.asInstanceOf[T]
case t: Exception => throw t
}.get
Note how I made the if part of the pattern match, doing that might help you insulate the problem.
I find it weird that your method does not terminate. You should probably try reducing the example to something very simple and start adding complexity little by little and see where it breaks.
Best of luck!
I wanted to check some best practices of Scala programming, since I am new to Scala. I read online about how Scala doesn't typically use exceptions except for "exceptional" circumstances (which doesn't include parameter checking). Right now in my project I am using a lot of require, so I am wondering what the better way of type checking would be.
For example, if I have a class
class Foo(String bar){
require(StringUtils.isNotEmpty(bar), "bar can't be empty")
}
what are my alternatives to checking bar? Do I create a companion object like so
Object Foo {
def apply(bar: String) = Try[Foo] {
bar match = {
case null => Failure("can't be null")
//rest of checks
case _ => Success[Foo]
}
}
Or should I use Option instead?
In addition, for scala methods, how do I check the parameter of the method? If I already return an Option, do I just return an empty Option if I get a bad parameter? Wouldn't that mean I have to check for an empty Option when I use the return of the method and wouldn't throwing an exception allow for a more specific message? (e.g. runtime exception can't use nulls).
I think the Success part of your companion object would return the Foo() object as well?
Object Foo {
def apply(bar: String) = Try[Foo] {
bar match = {
case null => Failure("can't be null")
//rest of checks
case _ => Success[Foo](new Foo(bar))
}
}
To use it you might do something with the Success you get from Foo(bar):
val hehe = Foo(bar).map(foo => foo.someString()).getOrElse('failed')
The Try methods will automatically wrap exceptions generated by someString() or whatever else that you're doing inside it inside Failures. If you wanted to check the parameters of foo.someString(), you'd do something similar to your apply() method. It isn't that much different than throwing exceptions on conditions but I think it's nicer cause the "catch blocks" would be in recover() or recoverWith(). You can always exit the Try using getOrElse() if your code wasn't designed to chain Trys from top to bottom.
I came across following java like code in Scala project. how to make it more Scala idiomatic with no side effects (exception handling appropriately) ?
I am thinking to use scalaz disjunction / (I know I can use Scala either too but like right biased more I guess). in a function there are a few such if checks(one above is one example) which throw one or the other type of exceptions. how to make such code more Scala idiomatic?
EDIT:
Question is not around how to convert Java null checks into Scala Idiomatic, which I am already doing. for e.g. following
hpi.fold(throw new Exception("Instance not found for id " + processInstanceId)) { h =>
val pi = new ProcessInstance(taskResponse)
Now return type of the existing function is some value say "ProcessInstance" for e.g. but in my opinion is misleading. caller would never know if this will throw an exception so my question is more around returning Either[Error,Value] from such functions. And if I have a few such exceptions being captured into a single function, how to accumulate them all and reflect into return type?
One thought might making processDefinition.getDiagramResourceName() return an Option, so you can then check whether the result is Some(x) or None.
Using scalaz and being idiomatic this is probably what I would end up with (subject to refactoring):
for {
pd <- Option(processDefinition.getDiagramResourceName()).\/>("Diagram resource could not be found")
} yield pd
So if you have null you get back Left("Diagram resource could not be found") otherwise you get Right(DiagramResourceName).
An approach based in case classes where different subclasses of names, references or labels may be defined,
trait ResourceName
case object MissingName extends ResourceName
case class DiagramResourceName(name: String) extends ResourceName
case class AnotherResourceName(ref: Int) extends ResourceName
processDefinition.getDiagramResourceName() match {
case DiagramResourceName(name) => println(s"name $name")
case MissingName => throw new ActivityException(errorMessage)
}