42

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 Answers 3

44

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).

Sign up to request clarification or add additional context in comments.

4 Comments

This is incorrect. Pattern matching does not necessarily use switches. In fact, there is the @switch annotation if you want to ensure that a tableswitch or lookupswitch is used.
@PaulDraper - I think you're reading something in there that I didn't say. If you want to always have the option of using Feature X, you need to always have the prerequisites met. I never said that switches were always used!
@RexKerr, I may have misread. But in any case, with or without stable identifiers, switches are sometimes used and sometimes not. (Specifically, without stable identifiers, they would not be used.) I can see why unstable identifiers can't easily work with switches, not why unstable identifiers can't easily work with pattern matching.
@PaulDraper - My point was that I suspected that it's easier that way: code analysis is simpler if identifiers are stable, and you need code analysis to tell that the tableswitch is possible. It's enabling, not essential.
17

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.

2 Comments

Exactly. If I can do it, why can't the compiler do it?
To make you aware of what you're doing.
10

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") } } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.