Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Misunderstanding of scala 'match' compile rules

I must have some basic misunderstanding of the Scala 'match' semantics or the compiler logic. This code:

val stageStart:Int = 0
val stageShutDown:Int = Int.MaxValue
val stageErrorReport:Int = Int.MinValue

def stageString(stage:Int):String = stage match {
  case stageStart       => "Start"
  case stageShutDown    => "End"
  case stageErrorReport => "Error"
  case _                => "Step " + String.valueOf(stage)
}

results in "Unreachable Code" errors on the last 3 'case' statements? If instead of the names you substitute the actual values (0, Int.MaxValue, Int.MinValue) it compiles -- but now I've hard-coded values that should be referenced by their names (for all the usual reasons). Since a 'val' can never change, shouldn't the first version also work?

like image 496
user1624503 Avatar asked Dec 03 '22 02:12

user1624503


2 Answers

There is a subtle yet important feature: If the identifier in case rules start with a lower-case letter, they're always treated as variables. So the first case matches always (storing stage into variable stageStart) and the rest 3 are unreachable. You need to define the constants with upper case letters as

val StageStart:Int = 0
val StageShutDown:Int = Int.MaxValue
val StageErrorReport:Int = Int.MinValue

def stageString(stage:Int):String = stage match {
  case StageStart       => "Start"
  case StageShutDown    => "End"
  case StageErrorReport => "Error"
  case _                => "Step " + String.valueOf(stage)
}

Then they won't be treated as variables but as constants to pattern-match on.

See also this answer for Naming convention for Scala constants?

like image 139
Petr Avatar answered Dec 05 '22 14:12

Petr


The issue is that when you use a variable that starts with a lowercase character, the pattern matcher thinks that you are trying to assign to that variable. So you get this behavior:

val stageStart = 0
val stage = 5
def stageString(stage: Int) = stage match {
  case stageStart => println(startStage)  // prints "5"
}

Of course, a pattern that is simply an assignable variable will match anything, so any subsequent case will be unreachable.

To solve this, you need to use a "stable identifier". This can be done by putting the lowercased variable in backticks:

val stageStart = 0
def stageString(stage: Int) = stage match {
  case `stageStart` => "Start"
}

or renaming the variable so that it starts with an uppercase character:

val StageStart = 0
def stageString(stage: Int) = stage match {
  case StageStart => "Start"
}

Also: String.valueOf(stage) should be rewritten as stage.toString.

like image 33
dhg Avatar answered Dec 05 '22 15:12

dhg