Given a method in UserService: update
, what's the best way to handle errors/exceptions here?
def update(...): Try[User]
In this way, I need to define my custom exceptions and throw them in the function body when needed. Most of these exceptions are business errors (e.g. user_id cannot be changed, etc). The point here is no matter what exception(s) are thrown (business error, network exception, DB IO exception, etc), treat them the same way and just return a Failure(err)
- let the upper layer handle them.
def update(...): Either[Error, User]
This is the exception-free way. In the function body it catches all possible exceptions and turns them into Error, and for business errors just return Left[Error]
.
Using Try
seems to be a more natural way to me as I want to handle errors. Either
is a more generic thing - Either[Error, T]
is just one special case and I think Try
is invented for this special case. But I also read that we should avoid using exceptions for error handling...
So, which solution is better, and why?
Exception handling is the mechanism to respond to the occurrence of an exception. Exceptions can be checked or unchecked. Scala only allows unchecked exceptions, though. This means that, at compile-time, we won't be able to know if a method is throwing an exception we are not handling.
The Try type represents a computation that may either result in an exception, or return a successfully computed value. It's similar to, but semantically different from the scala. util. Either type. Instances of Try[T] , are either an instance of scala.
In Scala Either, functions exactly similar to an Option. The only dissimilarity is that with Either it is practicable to return a string which can explicate the instructions about the error that appeared.
There's no silver bullet.
As you noted already, Try
is simply a more specialized version of Either
, where the Left
type is fixed to Throwable
.
Try
might be a good fit if you need to materialize exceptions thrown by external (perhaps java) libraries, as its constructor automatically catches them.
Another advantage of Try
is that it has map
and flatMap
, so you can use it directly in for-comprehensions, whereas with Either
you would have to explicitly project on the right
case.
Anyway, there's plenty of alternative implementations with a "right-bias", and probably the scalaz \/
type is the most popular one.
That being said, I typically use \/
or the almost equivalent Validation
(both from scalaz), as I like having the ability of returning errors that do not extend Throwable
.
It also allows for more precise error types, which is a huge win.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With