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?
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?
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.
Related
I have been working with Scala for close to a year, but every now and then I come across a piece of code that I don't really understand. This time it is this one. I tried looking into documents on "scala methods with generic parameter type", but I am still confused.
def defaultCall[T](featureName : String) (block : => Option[T])(implicit name: String, list:Seq[String]) : Option[T] =
{
val value = block match {
case Some(n) => n match {
case i : Integer => /*-------Call another method----*/
case s : String => /*--------Call another method----*/
}
case _ => None
}
The method is called using the code shown below :
var exValue = Some(10)
val intialization = defaultCall[Integer]("StringName"){exValue}
What I don't understand in the above described code is the "case" statement in the defaultCall method.
I see that when the exValue has a value and is not empty, the code works as expected. But in case I change the exValue to None, then my code goes into the "case _ = None" condition. I don't understand why this happens since the match done here is against the "variable" which would be either an Integer or a String.
What happens here is that when you pass a None it will match on the second case, which "catches" everything that is not an instance of a Some[T]:
block match {
case Some(n) => // Will match when you pass an instance of Some[T]
case _ => // Will match on any other case
}
Note that None and Some are two different classes that inherit from Option.
Also, the variable match is only done if the first match succeeds, otherwise not. To achieve the type checking in the first match you could do:
block match {
case Some(n: Int) => // do stuff
case Some(n: String) => // do stuff
case _ => // Will match on any other case
}
Hope that helps
I don't use pattern matching as often as I should.
I am matching a domain name for the following:
1. If it starts with www., then remove that portion and return.
www.stackoverflow.com => "stackoverflow.com"
2. If it has either example.com or example.org, strip that out and return.
blog.example.com => "blog"
3. return request.domain
hello.world.com => "hello.world.com"
def filterDomain(request: RequestHeader): String = {
request.domain match {
case //?? case #1 => ?
case //?? case #2 => ?
case _ => request.domain
}
}
How do I reference the value (request.domain) inside the expression and see if it starts with "www." like:
if request.domain.startsWith("www.") request.domain.substring(4)
You can give the variable you pattern matching a name and Scala will infer its type, plus you can put an if statement in you case expression as follows
def filterDomain(request: RequestHeader): String = {
request.domain match {
case domain if domain.startsWith("www.") => domain.drop(4)
case domain if domain.contains("example.org") | domain.contains("example.com") => domain.takeWhile(_!='.')
case _ => request.domain
}
}
Note that the order of the case expressions matters.
When writing case clauses you can do something like:
case someVar if someVar.length < 2 => someVar.toLowerCase
This should make pretty clear how grabbing matched values works.
So in this case, you would need to write something like:
case d if d.startsWith("www.") => d.substring(4)
If you're dead set on using a regex rather than String methods such as startsWith and contains, you can do the following:
val wwwMatch = "(?:www\\.)(.*)".r
val exampleMatch = "(.*)(?:\\.example\\.(?:(?:com)|(?:org)))(.*)".r
def filterDomain(request: String): String = {
request.domain match {
case wwwMatch(d) => d
case exampleMatch(d1, d2) => d1 + d2
case _ => request.domain
}
}
Now, for maintainability's sake, I wouldn't go this way, because a month later, I will look at this and not remember what it's doing, but that's your call.
you don't need pattern matching for that:
request.domain
.stripPrefix("www.")
.stripSuffix(".example.org")
.stripSuffix(".example.com")
I have a configuration value that matches to one of the values in a map and depending on to which it matches i take an action. Here is some sample code of what i am trying to do
val x = 1 // or 2 or 3
val config = Map("c1"-> 1, "c2"-> 2, "c3"-> 3)
x match {
case config("c1") =>
println("1")
case config("c2") =>
println("2")
case config("c3") =>
println("3")
}
Now this should print 1 because config("c1") evaluates to 1 but it gives error
error: value config is not a case class, nor does it have an unapply/unapplySeq member
case config("c1") =>
Similarly for the other 2 cases. Why should i have an unapply here? Any pointers?
An expression like that looks like an extractor, hence the message about unapply/unapplySeq methods. If you don't want to use an extractor but just want to match against a plain value, you need to store that value in a stable identifier - you can't use an arbitrary expression as a match case:
val case1 = config("c1")
x match {
case case1 => println("1")
...
}
To the best of my knowledge, in Scala, x match {case config("c1") gets translated to config.unapply(x) with the branching dependent on the result of the unapply method. As Imm already mentioned in his answer, this isn't the case for stable identifiers (literals and val), and I'd encourage you to use his solution.
Nevertheless, to show you how you could solve the problem using extractors, I'd like to post a different solution:
def main(args: Array[String]): Unit = {
object config {
val configData = Map("c1" -> 1, "c2" -> 2, "c3" -> 3)
def unapply(value: Int): Option[String] = configData find (_._2 == value) map (_._1)
}
1 to 4 foreach {
case config("c1") => println("1")
case config("c2") => println("2")
case config("c3") => println("3")
case _ => println("no match")
}
}
I changed the match for a foreach to show the different results, but this has no effect on the implementation. This would print:
1
2
3
no match
As you can see, case config("c1") now calls the unapply method and checks whether the result is Some("c1"). Note that this is inverse to how you'd use a map: The key is searched according to the value. However, this makes sense: If in the map, "c1" and "c2" both map to 1, then 1 matches both, the same way _ matches everything, in our case even 4 which is not configured.
Here's also a very brief tutorial on extractors. I don't find it particularly good, because both, the returned type and the argument type are Int, but it might help you understand what's going on.
As others have stated, with x match { case config("c1") => ..., scala looks for an extractor by the name of config (something with an unapply method that takes a single value and returns an Optional value); Making pattern matching work this way seems like an abuse of the pattern, and I would not use an extractor for this.
Personally, I would recommend one of the following:
if (x == config("c1"))
println("1")
else if (x == config("c2"))
println("2")
else ...
Or, if you're set on using a match statement, you can use conditionals like this:
x match {
case _ if x == config("c1") =>
println("1")
case _ if x == config("c2") =>
println("2")
case _ if x == config("c3") =>
println("3")
}
Not as clean; unfortunately, there isn't a way to invoke a method call literally where the extractor goes. You can use back-ticks to tell scala "match against the value of this variable" (rather than default behavior, which would yield the value named as that variable):
val (c1,c2,c3) = (config("c1"), config("c2"), config("c3"))
x match {
case `c1` =>
println("1")
case `c2` =>
println("2")
case `c3` =>
println("3")
}
Finally, if your goal is to reverse-apply a map, maybe try this instead?
scala> Map("a" -> 1).map { case (k,v) => (v,k) }
res0: scala.collection.immutable.Map[Int,String] = Map(1 -> a)
These two statements behave the same :
def getNum(inp: String): Double = inp match { case "" | null => 0.0 case _ => inp.toDouble }
def getNum(inp: String): Double = inp match { case "" | null => 0.0 case x => x.toDouble }
Question is, where should either one be used and is one essentially better than the other?
The bytecode for the two is identical, so you can use whichever you prefer stylistically.
Note that in some cases you may have a complex expression as the source of your value to match, which makes it harder to refer to. Thus you may have greater consistency of style with the case x => x.toDouble form.
regarding the following scala code, functions m2a and m2b apparently differ only by the case of the parameter, ie abc vs Abc. This seems to make some difference in the result as per example below. When running it with a recent 2.8 compiler, it results in the following (I would have expected all true). Any insights would be appreciated.
m1=true
m2a=true
m2b=false
m3=true
code
package sample
import scala.xml._
object ParamTest extends Application {
def m1(n:Node, abc:String):Boolean = {
n == <id>{Text(abc)}</id>
}
def m2a(n:Node, Abc:String):Boolean = n match {
case <id>{Text(Abc)}</id> => true
case _ => false;
}
// why does this one not work?
def m2b(n:Node, abc:String):Boolean = n match {
case <id>{Text(abc)}</id> => true
case _ => false;
}
def m3(n:Node, abc:String):Boolean = n match {
case Elem(_,"id",_,_, c #_ *) => {
c contains Text(abc)
}
}
def runner(n:Node, f:(Node, String)=>Boolean):Boolean = {
f(n, "x") && !f(n, "y") && !f(n, "");
}
val x = <id>x</id>
println("m1="+runner(x, m1));
println("m2a="+runner(x, m2a));
println("m2b="+runner(x, m2b));
println("m3="+runner(x, m3));
}
The trick here is in how Scala handles variables in case expressions. Lowercase variables in case expressions are taken by the compiler to introduce new variables, which are then pattern matched against. Thus in method m2b, the method parameter "abc" is actually unused. The case expression variable "abc" will match any string, since it is not otherwise constrained. Thus "y" is successfully matched in the first case of m2b. Uppercase variables in case expressions do not introduce new variables, so in m2a the match behaves as you expected.
The easiest way to match against the value of a lowercase variable is to wrap it in backquotes. Thus
def m2b(n:Node, abc:String):Boolean = n match {
case <id>{Text(`abc`)}</id> => true
case _ => false;
}
will give you the results you expected.
In pattern matching, identifiers in the pattern that start with a lower-case letter are taken to be free pattern variables that can be bound to values in the target of the match. Those that start with upper-case letters are so-called stable identifiers and must already be bound in the match expression's context and the value of that binding must equal the subexpression of the match target at the point in that value corresponding to that stable identifier's placement within the pattern expression.
Additionally, and relevant in this particular example, pattern variables (the lower-case names) will shadow any existing binding of the same name that is in effect in the context of the match expression (including the expression supplying the match target value).