Scala, Slick, Cats - How to map different SQL errors with OptionT?

I have a simple slick query which run on database:

def method(): Future[Either[Error, MyCustomDTO]] = 

The problem is with .toRight. I would like to map it to sifferent errors depends on what was returned by database. E.g.

case FOREIGN_KEY_CONSTRAINT_VIOLATION.toString => constraintError()
case UNIQUE_CONSTRAINT_VIOLATION.toString => uniqueError()
case _ => dataNotFound()

I tried to do match case in .toRight(), but it does not work:

.toRight(error => error.asInstanceOf[PSQLException].getSQLState match { .... })

I'm wondering what is the best possibility to map different errors here in a correct way?


  • Take a look at the signature of toRight(...)

    def toRight[L](left: => L)(implicit F: Functor[F]): EitherT[F, L, A] =
      EitherT(cata(Left(left), Right.apply))

    both of these take by-name parameter - on other words they are special syntax of () => ... where () => ... in definition and ...() in application are inserted for you. Why? Because on toLeft/toRight in OptionT assume that you are handling the kind of error that is expresses by Option - that is None. Since there is no need to pass _: None.type => it uses by-name parameter instead.

    If you want to handle the error you have to handle it inside F[A] - by providing the right type class (ApplicativeError[F, Throwable]/MonadError[F, Throwable]) which would allow calling handleError/handleErrorWith/redeem etc

    // F: ApplicativeError[F, Throwable]
    // fa: F[MyCustomDTO]
    F.redeem(fa)(a => Right(a), error => Left(error match { ... }))
    // or with extension methods for AplicativeError
    fa.redeem(a => Right(a), error => Left(error match { ... }))
    // asRight is extension method creating Either[Error]).handleError(e => Left(...))

    since your F seem to be Future, and since you have it wrapped in OptionT I guess it worked better if you did something like:

      .handleError { error =>
        Left(error.asInstanceOf[PSQLException].getSQLState match {

    You could also give up on OptionT and do: // EitherT[Future, Throwable, ...]
      .leftMap { error =>
        error.asInstanceOf[PSQLException].getSQLState match {
      .subflatMap { option =>