Visiedo
Visiedo

Reputation: 387

How to pass an Exception, with default "cause" as function parameter in Scala

I am writing a micro-service in Scala, and I am processing a response from a server, handling errors by throwing different exceptions.

The error processing is basically the same for each error, log a warning message and throw an exception. The only thing that changes is the kind of custom exception I am generating, so I did not want to repeat the same code all over my "case" statements, therefore I decided to use a method, accepting the exception as parameter.

config.httpClient.apply(request).map(response => {
  response.getStatusCode() match {
    case Status.Ok.code =>
      <Do stuff to process the response>
    case Status.BadRequest.code =>
      errorReporting(response.status.code, response.contentString, BadRequestException)
    case Status.Unauthorized.code =>
      errorReporting(response.status.code, response.contentString, UnauthorizedException)
    case _ =>
      errorReporting(response.status.code, response.contentString, GeneralException)
  }
})

The exceptions are defined as case classes:

case class GeneralException(private val message: String,
                            private val cause: Throwable = None.orNull) extends Exception (message, cause)

case class BadRequestException(private val message: String,
                               private val cause: Throwable = None.orNull) extends Exception(message, cause)

case class UnauthorizedException(private val message: String,
                                 private val cause: Throwable = None.orNull) extends Exception(message, cause)

And the errorReporting method here:

def errorReporting(errorCode: Int, errorMessage: String, ex: (String, Throwable) => Throwable) = {
  val message = s"Auth server response code: ${errorCode}, " + errorMessage
  warn(message)
  throw ex(message, None.orNull)
}

I defined my exceptions with a default value for "cause". The problem is that I do not find a way to tell "errorReporting" that Throwable is an optional parameter, therefore I am forced to throw the exception with throw ex(message, None.orNull) which I do not find very elegant.

This entry Invocation of methods with default parameters in scala higher-order function tells me it is not possible to pass default parameters to high order functions.

Is there an alternative to solve this in a better way?

Upvotes: 0

Views: 1864

Answers (1)

Oleg Pyzhcov
Oleg Pyzhcov

Reputation: 7353

You can specify default parameters in a custom trait. Companion objects for your exception class can extend that trait, so they get default parameters too. Note that in that case you are not required to use default parameters inside class definition.

Then just make your function accept instances of that trait

// Extends clause is optional, tho without it you lose
// methods like "compose" on companion 
trait OptionalCauseConstructor extends ((String, Throwable) => Exception) {
  def apply(message: String, cause: Throwable = null): Exception
}

case class GeneralException(private val message: String,
                            private val cause: Throwable) extends Exception (message, cause)

// autogenerated apply matches required signature, so implementation is automatic
object GeneralException extends OptionalCauseConstructor


case class BadRequestException(private val message: String,
                               private val cause: Throwable) extends Exception(message, cause)

object BadRequestException extends OptionalCauseConstructor

// etc...


def raiseError(ctor: OptionalCauseConstructor) = {
    val cause = ctor("cause") // cause is default
    throw ctor("foo", cause)  // cause is supplied
}

raiseError(GeneralException)
// also, Scala 2.12 allows for SAM implementation
raiseError(new Exception(_, _))

Upvotes: 1

Related Questions