3

In order to handle exceptions in Scala, I prefer avoiding basic try/catch and benefit from functional thinking with Validation from Scalaz (similar to Either type in certain cases).

My application exposes some services. Imagine this method (making no real sense, but good for the concept) in my service layer. It associates a Buyer (purchaser) with his new Car and returns the Car containing this association if all rules are passed with success:

def create(carDTO: CarDTO, buyerDTO: BuyerDTO): Validation[Either[TechnicalFailure, List[CarCreationFailure]], CarDTO]

Explanation: Creating a Car may lead to one of both exceptions types:

  • Technical failures (when databases crashes for instance) wrapping Throwable exceptions.
  • Business failures (custom application's rules preventing from inconsistence, for instance, a Car with an illegal status). CarCreationFailure is one and of course could be extended by more precised failure.

My question is especially focus on client side and deals specifically with more than one potential Business Failure.

Should the return type Validation[Either[TechnicalFailure, List[CarCreationFailure]], CarDTO] be replaced by the less cumbersome: ValidationNel[Throwable, CarDTO]

Note here ValidationNel (accumulating errors/exceptions into a NonEmptyList).

A drawback would be that a new reader couldn't, at first glance, guess that this method returns either TechnicalFailure or CarCreationFailure (subclass of BusinessFailure so); just a too frightening Throwable.

He'd be forced to apply a pattern matching on every Throwable types contained within my application to in order to be sure to not forget anyone... => messy.

What is the cleanest way among those solutions, or maybe...other one?

Mik378
  • 21,881
  • 15
  • 82
  • 180
  • 2
    This is more like a "code review" question than SO question. Anyway, why not create a supertrait for both `TechnicalFailure` and `CarCreationFailures` (which would wrap `List[CarCreationFailure]`)? Then your type system knows what can come back and you can pattern match to deal with the cases (and you can stick everything in a `Validation`). – Rex Kerr Apr 24 '13 at 14:45
  • 1
    You almost _certainly_ do not want to catch all of `Throwable` unless you're writing something like a servlet container. Look at the `Error` half of `Throwable` an ask yourself whether you really want to (and can) handle all of them. – Randall Schulz Apr 24 '13 at 14:56
  • 1
    Agree with @RexKerr, this question is rather general and it's hard to give a specific answer. Certainly I would think about changing your first propesed result type to `ValidationNel[Either[TechnicalFailure, CarCreationFailure], CarDTO]`. – pagoda_5b Apr 24 '13 at 15:23
  • @Randall Schulz Actually, one client of my service is a `Controller` like any Servlet for instance. I don't want to swallow `Error` or `Runtime` happened at server side since I expect `Controllers` to act accordingly following my custom rules, and not basic ones provided by any container or any frameworks. – Mik378 Apr 24 '13 at 17:58

2 Answers2

2

In cases like these, I personally use a custom class called Tri which works like both Try and Either:

sealed trait Tri [+Value, +Problem] { ... }
final case class Good[+Value](v: Value) extends Tri[Value, Nothing] { ... }
final case class Bad[+Problem](p: Problem) extends Tri[Nothing, Problem] { ... }
final case class Ugly(th: Throwable) extends Tri[Nothing, Nothing] { ... }

with appropriate methods to handle the most common operations one wants. In this case, I'd simply return to the client

Tri[CarDTO, List[CarCreationFailure]]

and have the exceptions wrapped into a custom exception class as needed. This both is up-front about what may or may not happen when the code itself is working normally, and leaves unstated--as usual--exceptional conditions that need to be handled sooner or later, including things like database errors. Then you just e.g.:

create(car, buyer) match {
  case Good(dto) => // Yay
  case Bad(xs) =>   // Handle list of car creation failures
  case Ugly(th) =>  // Handle exceptions
}

I am not aware of any similar functionality in Scalaz7, though you can build the pieces (as you have, though why not use \/?) more easily than in Scalazless Scala, so you might be less motivated to create this kind of tripartite error handling class.

kiritsuku
  • 52,967
  • 18
  • 114
  • 136
Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
  • Actually, with this solution, you simulate an `Either` concept focused on 3 elements, rather than the basic `Right`and `Left` => Why not :). Besides, what is the meaning of `\/` you have suggested? Thanks :) – Mik378 Apr 24 '13 at 18:12
  • Ok, I've just seen look at the source code, `\/` acts as a "disjunction. – Mik378 Apr 24 '13 at 18:16
  • Finally, I prefer following your solution in comment: wrapping `TechnicalFailure` and `CreationalFailures` into a parent `trait` and still benefit of Scalaz7's `Validation`. May you provide a little sample with the famous `\/` in order to, maybe, improve my solution? Thanks a lot by the way. – Mik378 Apr 24 '13 at 19:33
  • @Mik378 - I don't really use Scalaz since it mostly solves problems that I don't have much trouble solving myself, and makes more difficult things I already find difficult. (Not a general indictment of Scalaz, by the way--it just doesn't suit me personally, given what I'm doing now.) So I'm not confident my example would be ideal. – Rex Kerr Apr 24 '13 at 19:39
1

We use something similar (simpler and less elegant) to what Rex Kerr uses.

All business exceptions/errors are wrapped in a custom BusinessException, all other errors are throwing exceptions of various kind.

We use the basic Try and a servlet will have code looking like (much simplified)

    ( req getParameter "action" match {

        case null          ⇒ Failure( new BusinessException( "Missing required parameter 'action'" ) )
        case "doThis"  ⇒ doThis()
        case "doThat"  ⇒ doThat( req )
        case x         ⇒ Failure( new BusinessException( "Uknown Action '" + x + "'" ) )
    } ) match {

        case Success( someResponse ) ⇒ //send 200 with some response
        case Failure(t) if t.isInstanceOf[BusinessException] => //send 400 with exception message 
        case Failure( t )        ⇒ //send 500 with exception message
    }

Methods like doThishave signatures of the type

def doThis(): Try[ Option[ String ] ] = Try {
    //do stuff
}

The advantage of this simple mechanism is that it is easy to wrap existing Java code throwing exceptions, which is our use case.

Bruno Grieder
  • 28,128
  • 8
  • 69
  • 101