40
votes

The following

def mMatch(s: String) = {
    var target: String = "a"
    s match {
        case `target` => println("It was " + target)
        case _ => println("It was something else")
    }
}

does not compile:

error: stable identifier required, but target found. case target => println("It was " + target)

Why does Scala require a val not a var. I guess "Because" would be an acceptable answer but I have the feeling there is a deeper reason I am missing.

3

3 Answers

43
votes

I suspect that it's to enable table-switching optimizations for those cases where it is possible (without piles of checking to see whether it's valid). For example, with the code

class Sw {
  def m(i: Int) = {
    val a = 3
    val b = 2
    val c = 1
    i match {
      case `a` => 0
      case `b` => -1
      case `c` => 4
      case _ => 2
    }
  }
}

you get the bytecode

public int m(int);
  Code:
   0:   iconst_3
   1:   istore_2
   2:   iconst_2
   3:   istore_3
   4:   iconst_1
   5:   istore  4
   7:   iload_1
   8:   istore  5
   10:  iload   5
   12:  tableswitch{ //1 to 3
        1: 48;
        2: 44;
        3: 52;
        default: 40 }
   40:  iconst_2
   41:  goto    53
   44:  iconst_m1
   45:  goto    53
   48:  iconst_4
   49:  goto    53
   52:  iconst_0
   53:  ireturn

which would be much more complicated to do if you used vars (you'd have to detect whether they had changed to know whether that table expression was still valid).

16
votes

There's nothing to stop you just turning your var into a val before using it in the match:

def mMatch(s: String) = {
    var target: String = "a"
    val x = target
    s match {
        case `x` => println("It was " + target)
        case _ => println("It was something else")
    }
}

works perfectly fine.

10
votes

My guess is stable identifiers are required as a simplification to avoid situations where the variable changes inside the pattern matching itself. This would require clarification in the spec, and disrupt optimizations as Rex Kerr mentions.

var x: String = "a"
"b" match {
  case `x` if { x = "b"; true } => println("success")
}

Edit. But this explanation is not completely satisfactory, because the stable identifier could refer to a mutable object,

val x = collection.mutable.Seq(2)
def f(y: Seq[Int]) {
    y match {
      case `x` if { x(0) = 3; true } => println("success")
    }
}
f(Seq(2)) // success
f(Seq(2)) // failure

Note that a stable identifier is not necessarily known statically. For example, the following is fine,

def f(x: Int) {
  1 match { case `x` => println("hi") }
}