0

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?

Visiedo
  • 377
  • 5
  • 9
  • Maybe this question is usefull to you: https://stackoverflow.com/questions/11982474/case-classes-with-optional-fields-in-scala/11982762 – tgr Jul 17 '17 at 10:58
  • `None.orNull` is a long way to write `null`. For optional parameters, use `cause: Option[Throwable] = None` – Assen Kolov Jul 17 '17 at 11:58
  • @T.Grottker not solving my issue, still useful. Thanks! – Visiedo Jul 18 '17 at 07:25
  • @AssenKolov you might be right, although I suspect there might be a difference. I've got it from here https://stackoverflow.com/questions/38243530/custom-exception-in-scala/38250743 – Visiedo Jul 18 '17 at 07:32
  • Check the scaladoc for Option.orNull: `Returns the option's value if it is nonempty, or null if it is empty. Although the use of null is discouraged, code written to use option must often interface with code that expects and returns nulls.` For `None` it returns `null`. – Assen Kolov Jul 18 '17 at 12:00

1 Answers1

1

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(_, _))
Oleg Pyzhcov
  • 7,323
  • 1
  • 18
  • 30