Home > Enterprise >  Check two specific exceptions in a match expression in Scala
Check two specific exceptions in a match expression in Scala

Time:12-08

So I have a Try block in Scala

  def fromString(s: String): Option[Pitch] = scala.util.Try {
    val (pitchClassName, octaveName) = s.partition(c => !c.isDigit)
    val octave = if octaveName.nonEmpty then octaveName.toInt else 5
    Pitch(pitchClassIndex(pitchClassName)   octave  * 12)
  } match {
    case scala.util.Success(value) => Some(value)
    case scala.util.Failure(e) => 
    case scala.util.Failure(e) => throw e
  }

Now, I know there's a lot of code here that needs an explanation. For the purpose of this question though what is needed to know is this:

When creating a Pitch instance with a given note like "D#4" there can be two different exceptions that I want to specifically handle. The first is if the Map pitchClassIndex cannot find the given key pitchClassName, the second is if Pitch argument is outside of a given range.

pitchClassIndex:

val pitchClassIndex: Map[String, Int] = pitchClassNames.zipWithIndex.toMap

pitchClassNames:

val pitchClassNames: Vector[String] = Vector("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B")

So now we enter s: String and check whether it has a digit, if so split it into the tuple val (pitchClassName, octaveName) If s = "D#4" the tuple is now ("D#", "4") val octave = 4 So now when creating our Pitch instance we'll get the value in pitchClassIndex: Map[String, Int] from the key pitchClassName: String = "D#" (Note here that if pitchClassName does not correspond to a value in pitchClassNames we'll get an exception) we'll then add octave: Int = 4 first multiplied by 12. Pitch(51) Now the two first lines of Pitch looks like this:

case class Pitch(nbr: Int):
  assert((0 to 127) contains nbr, s"Error: nbr $nbr outside (0 to 127)")

So if arguments passed into Pitch is outside of the range (0 to 127) then throw and AssertionError with error message.

So right now we have two cases where it can throw an exception, the first is the assert in the beginning. The second is if pitchClassIndex(pitchClassName) takes a key that is not included in pitchClassNames: Vector[String] for example "K#". Then it will throw a NoSuchElementException with message: "key not found: K#"

Now as you can see I have an empty Failure case in the match expression where I test the Try statement, in this case I wanna check whether exception e is either the AssertionError or the NoSuchElementException and if it is one of these I wanna print some special text. The last case is for other exceptions. However I'm not quite sure how to test this? Is there something I can write inside the Failure like (e: ???) Any ideas?

CodePudding user response:

You could add two case clauses for the two exceptions

case Failure(e: AssertionError) => 
  // do something
case Failure(e: NoSuchElementException) => 
  // do something else
case Failure(e) =>
  // goes here for other exceptions

If you want to combine them into just one case, you can no longer capture details in the variable e, so that may not be an option:

case Failure(_: AssertionError) 
      | Failure(_: NoSuchElementException)     => 
   // cannot use `e` anymore 

I suppose you could resort to .isInstanceOf

case Failure(e) 
  if e.isInstanceOf[AssertionError] ||
     e.isInstanceOf[NoSuchElementException] =>
   // now `e` is Throwable in here  
  • Related