Can I use switch-case for pattern matching on enumerations?
I tried
import scala.annotation.switch
object Foo extends Enumeration {
val First = Value
val Second = Value
val Third = Value
}
object Main {
def foo(x: Foo.Value) = (x: #switch) match {
case Foo.First => 1
case Foo.Second => 2
case Foo.Third => 3
}
}
but get the following warning (Scala 2.11.4):
warning: could not emit switch for #switch annotated match
def foo(x: Foo.Value) = (x: #switch) match {
I then tried defining the enumeration in Java instead, since Java's enums are different than Scala's Enumeration. Still no luck.
It #switch pattern matching only available on primitive types?
To complete Regis answer, in Scala In Depth, Joshua Suereth states that the following conditions must be true for Scala to apply the tableswitch optimization:
The matched value must be a known integer.
The matched expression must be “simple.” It can’t contain any type checks, if statements, or extractors.
The expression must also have its value available at compile time.
There should be more than two case statements.
Foo object does not match to any of above criteria though it is not a subject for tableswitch optimisation.
The point of the switch annotation is to make sure that your match is compiled into a tableswitch or lookupswitch JVM instruction. Those instructions only work on ints, which means that the switch annotation will only have any effect on types that can safely fit in an Int. Meaning Int itself as well as Char, Byte, Short and Boolean. In addition, the values you match against have to be literal values (as opposed to values stored in a val). Given that an Enumeration is a reference value, they are not compatible with switch annotation. The restriction about literal values actually means that there is probably no way to use this ennotation for Short and Byte, for purely syntactic reasons as there is no support for literal shorts and bytes in scala: you have to use a literal int along with a type ascription as in 123: Byte, but this is not accepted as a pattern.
So that leaves only Int, Char and Boolean as valid types (the usefulness of using #switch for a boolean value is dubious to say the least)
Related
Why does pattern matching in Scala work for String and AnyVals such as Int?
Usually we see things like Case classes or Extractors...
Extractors and case classes are used just for two of 13 kinds of patterns in Scala, "Extractor patterns" and "Constructor patterns" respectively. You can't use Int or String in this kind of pattern (case String(x)). But you can use them in other kinds:
Typed patterns, as in case x: String. In that case there is nothing special about String, you can do the same with any class (but there is something special about Int and other primitives: case x: Int actually checks if the received object is a java.lang.Integer in most cases).
Literal patterns, as in case 0 or case "". Again, nothing special about strings, this works for all literals.
java.lang.String is enriched with scala.collection.immutable.StringOps (http://www.scala-lang.org/api/2.11.8/#scala.collection.immutable.StringOps) which mix scala.collection.immutable.StringLike (http://www.scala-lang.org/api/2.11.8/#scala.collection.immutable.StringLike) in. There you can find complementary methods, like apply.
String is a bit special as well, you can convert it to list of Chars, and use List extractors then like case List(a,b) or case x:xs on a String, bearing in mind that a and b will be Chars; x: Char and xs: List[Char]
All primitive types have Rich* classes (e.g. scala.runtime.RichBoolean, scala.runtime.RichByte).
Value classes mechanics is used to enrich all of the above mentioned types (http://docs.scala-lang.org/overviews/core/value-classes.html). In compile time they have a wrapper type, like RichBoolean or RichInt but in runtime they are pure Boolean or Int types. In such way avoiding overhead of creating runtime objects.
val x: Any = 5
def f[T](v: T) = v match {
case _: Int => "Int"
case _: String => "String"
case _ => "Unknown"
}
You don't have to define unapply in class to able to use that class in switch/case-style pattern matching. unapply is used to deconstruct object, so, if you want to match switch in List-style (case x:xs), you should use unapply/unapplySeq. Good example here are regexps, are they are constructed from strings -- "something".r (note .r in the end).
I'm using macro annotations to inspect the fields of a class and add a member based on those fields.
e.g.
#AddVal
class A(x: Int)
expands to
class A(x: Int){
val get: Int = x
}
After extracting theValDef, it's tpe field still null so to get the type I have two options:
1) If I call .toString on the type tree, I can see the type, but now I've lost some type-safety
2) If I use c.typecheck on the type tree, I can get the type, but only if it's 1 level deep. List[List[Int]] comes back as List[List[...]]
val fieldType = c.typecheck(q"type T = ${f.tpt}") match {
case x # TypeDef(mods, name, tparams, rhs) => rhs.tpe
}
So, is there a way to recursively typecheck polytypes?
I tried typechecking rhs again but I got The argument types of an anonymous function must be fully known and I'm not sure how to resolve that.
Thanks for taking a look,
Julian
I incorrectly attributed this error to the macro, when in fact there was another underlying macro (a type provider macro) that was failing to provide the proper nested type (in this case the Int).
Given this definition in Scala:
class Foo(val n: Int)
object Foo {
def unapply(foo: Foo): Option[Int] = Some(foo.n)
}
This expression compiles and returns ok:
new Foo(1) match {
case Foo() => "ok"
}
Why does this even compile? I would expect that an extractor with Option[T] implies matching patterns with exactly one argument only.
What does the pattern Foo() mean here? Is it equivalent to Foo(_)?
In other words, what is the language rule that enables the experienced behavior.
Today (in 2.11 milestone) you get the error:
<console>:15: error: wrong number of patterns for object Foo offering Int: expected 1, found 0
case Foo() => "ok"
^
I encountered this when adding Regex.unapply(c: Char). At some point, the case you point out was accepted, then later rejected. I remember I liked the idea that if my extractor returns Some(thing), then the Boolean match case r() would work the same as case r(_).
What works is in the scaladoc of unapply(Char) :
http://www.scala-lang.org/files/archive/nightly/docs-master/library/#scala.util.matching.Regex
Section 8.18 of the Scala Language Reference discusses this type of pattern matching. According to the reference, for a pattern like Foo(), it should only match if unapply returns a boolean. If unapply returns Option[T] for some T that isn't a tuple, then the pattern must include exactly one parameter, e.g. Foo(_). Unless I'm really misunderstanding what is happening here, it looks like this is an edge case where the compiler violates the spec.
I have a little problem in pattern matching an object in Scala when it is parameterized with a fully qualified class name. This is based on Scala 2.9.0.1. Anyone knows what's wrong with this code?
scala> "foo" match {
| case y : Seq[Integer] =>
| case y : Seq[java.lang.Integer] =>
<console>:3: error: ']' expected but '.' found.
case y : Seq[java.lang.Integer] =>
Why does the first version work, but the latter fail? The problem only seems to occur when a fully qualified classname is used for the parameterization.
From the Scala Language Specification, section 8.1 Patterns, the identifier after the : needs to be what is referred to as a Type Pattern, defined in Section 8.2:
Type patterns consist of types, type variables, and wildcards. A type
pattern T is of one of the following forms:
...
A parameterized type pattern T [a(1), . . . , a(n)], where the a(i) are
type variable patterns or wildcards _. This type pattern matches all
values which match T for some arbitrary instantiation of the type
variables and wildcards. The bounds or alias type of these type
variable are determined as described in (§8.3).
...
A type variable pattern is a simple identifier which starts with a
lower case letter. However, the predefined primitive type aliases
unit, boolean, byte, short, char, int, long, float, and double are not
classified as type variable patterns.
So, syntactically, you can't use a fully qualified class as a type variable pattern IN THIS POSITION. You can however, use a type alias, so:
type JavaInt = java.lang.Integer
List(new java.lang.Integer(5)) match {
case y: Seq[JavaInt] => 6
case _ => 7
}
will return 6 as expected. The problem is that as Alan Burlison points out, the following also returns 6:
List("foobar") match {
case y: Seq[JavaInt] => 6
case _ => 7
}
because the type is being erased. You can see this by running the REPL, or scalac with the -unchecked option.
In fact your first example doesn't work either. If you run the REPL with -unchecked, you'll see the following error:
warning: non variable type-argument Integer in type pattern Seq[Integer] is unchecked since it is eliminated by erasure
So you can't actually do what you are trying to do - at run-time there's no difference between a List[Integer] and a List[AnythingElse], so you can't pattern-match on it. You might be able to do this with a Manifest, see http://ofps.oreilly.com/titles/9780596155957/ScalasTypeSystem.html#Manifests and http://www.scala-blogs.org/2008/10/manifests-reified-types.html
In Scala it is possible formulate patterns based on the invididual characters of a string by treating it as a Seq[Char].
An example of this feature is mentioned in A Tour of Scala
This is the example code used there:
object RegExpTest1 extends Application {
def containsScala(x: String): Boolean = {
val z: Seq[Char] = x
z match {
case Seq('s','c','a','l','a', rest # _*) =>
println("rest is "+rest)
true
case Seq(_*) =>
false
}
}
}
The problem I have with this is the third line of the snippet:
val z: Seq[Char] = x
Why is this sort of cast necessary? Shouldn't a String behave like a Seq[Char] under all circumstances (which would include pattern matching)? However, without this conversion, the code snippet will not work.
There is some real abuse of terminology going on in the question and the comments. There is no cast in this code, and especially "So basically, this is a major concession to Java interoperability, sacrificing some type soundness" has no basis in reality.
A scala cast looks like this: x.asInstanceOf[Y].
What you see above is an assignment: val z: Seq[Char] = x
This assignment is legal because there is an implicit conversion from String to Seq[Char]. I emphasize again, this is not a cast. A cast is an arbitrary assertion which can fail at runtime. There is no way for the implicit conversion to fail.
The problem with depending on implicit conversions between types, and the answer to the original question, is that implicit conversions only take place if the original value doesn't type check. Since it's perfectly legal to match on a String, no conversion takes place, the match just fails.
Not 100% sure if this is correct, but my intuition says that without this explicit cast you would pattern match against java.lang.String, which is not what you want.
The explicit cast forces the Scala compiler to use Predef.stringWrapper implicit conversion; thus, as RichString extends Seq[Char], you are able to do a pattern match as if the string were a sequence of characters.
I'm going to echo everything that andri said. For interoperability, Scala strings are java.lang.Strings. In Predef, there's an implicit conversion from String to RichString, which implements Seq[Char].
A perhaps nicer way of coding the pattern match, without needing an intermediate val z to hold the Seq[Char]:
def containsScala(x: String): Boolean = {
(x: Seq[Char]) match {
...
}
}