129

Take the following function:

def fMatch(s: String) = {
    s match {
        case "a" => println("It was a")
        case _ => println("It was something else")
    }
}

This pattern matches nicely:

scala> fMatch("a")
It was a

scala> fMatch("b")
It was something else

What I would like to be able to do is the following:

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

This gives off the following error:

fMatch: (s: String)Unit
<console>:12: error: unreachable code
               case _ => println("It was something else")

I guess this is because it thinks that target is actually a name you'd like to assign to whatever the input is. Two questions:

  1. Why this behaviour? Can't case just look for existing variables in scope that have appropriate type and use those first and, if none are found, then treat target as a name to patternmatch over?

  2. Is there a workaround for this? Any way to pattern match against variables? Ultimately one could use a big if statement, but match case is more elegant.

Henry Henrinson
  • 5,203
  • 7
  • 44
  • 76
  • Related: http://stackoverflow.com/questions/7083502/why-cant-a-variable-be-a-stable-identifier – Dave L. Apr 21 '13 at 13:56
  • 3
    I believe this question, code and answers are outdated as of Scala 2.12.x. It would be nice if the version to which is applies was mentioned as part of the question. – conny Jun 26 '17 at 16:22

2 Answers2

257

What you're looking for is a stable identifier. In Scala, these must either start with an uppercase letter, or be surrounded by backticks.

Both of these would be solutions to your problem:

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

def mMatch2(s: String) = {
    val Target: String = "a"
    s match {
        case Target => println("It was" + Target)
        case _ => println("It was something else")
    }
}

To avoid accidentally referring to variables that already existed in the enclosing scope, I think it makes sense that the default behaviour is for lowercase patterns to be variables and not stable identifiers. Only when you see something beginning with upper case, or in back ticks, do you need to be aware that it comes from the surrounding scope.

Ben James
  • 121,135
  • 26
  • 193
  • 155
  • 5
    I bet this comes from Erlang, where variables start with a capital letter and symbols with a lower-case. – Emil Ivanov Aug 16 '11 at 12:27
  • 12
    Notice that `target` is a value (`val`), and not a variable (`var`). It doesn't work with variables. – Luigi Plinge Aug 16 '11 at 12:54
  • Uppercase? Shades of FORTRAN. Weak, Martin, weak. – Michael Lorton Aug 16 '11 at 18:52
  • 15
    @Emil Actually, capitalized identifiers in Scala denote constants. So a pattern matching on an uppercase identifier is taken to mean comparing to a constant. It seriously helps with stuff like `Nil`, which _I_ bet is the real reason. – Daniel C. Sobral Aug 16 '11 at 22:11
  • Seems like one can't use `this` as a stable identifier to pattern match against it, only way seems to be using an equality guard like `case x if x == this =>`. Probably a syntactical limitation, otherwise it should semantically work at least within `object`s. – Nader Ghanbari Oct 11 '18 at 21:16
  • This rule also makes it easy to shadow unwanted variables, e.g. `NonEmptyList.from(list) match { case None => Left("List was empty"); case Some(list) => /* Re-use the name 'list' for the NonEmptyList, so the original List can't be referenced accidentally */ }` – Warbo Oct 15 '20 at 11:21
2

You can also assign it to a temporary variable inside the case and then compare it, that will also work

def mMatch(s: String) = {
    val target: String = "a"
    s match {
        case value if (value ==target) => println("It was" + target) //else {} optional
        case _ => println("It was something else")
    }
}


Raptor0009
  • 258
  • 4
  • 14