Can I suppress the unchecked warning in this case? - scala

I have (simplified from actual code):
class Def[T]
object Fun {
def unapply[A,B](d: Def[A => B]): Option[A => B] = ???
}
def isFun(d: Def[_]) = d match {
case Fun(f) => true
case _ => false
}
This produces a warning:
non-variable type argument A => B in type pattern TypeName.this.Def[A => B] is unchecked since it is eliminated by erasure
I've tried placing #unchecked after Fun(f), but this produces an error; and after f, which doesn't suppress the warning. Is there any way to remove this warning?

I hope I'm wrong, but after browsing the SLS, I don't believe you can apply the annotation in the right place without changing your code.
Since annotations "may apply to definitions or declarations, types, or expressions" (Chapter 11), you need one of those for your annotation application to be syntactically correct. The two most likely candidates here seem to be either a type or an expression. However, looking at Chapter 8 Pattern Matching, it seems
Fun(f)
i.e. the statement where you need to apply the annotation to, is neither, since it looks like it corresponds to:
StableId '(' varid ')'
none of which seem to fit the bill for either an expression or a type (or any other valid annotation target).

First, can you change the signature of def isFun(d: Def[_]) to def isFun[A,B](d: Def[A=>B])?
If not, the issue is the type erasure. On the JVM, you can't have this code :
trait Foo {
def doStuff(xs:List[Int])
def doStuff(xs:List[Long])
}
At the runtime, you don't have the info of the generics, their type is erased.
So, in your case, the problem is that you can't pattern match against a generic.
Let's see that example in a Scala Worksheet:
object Fun {
def unapply[A, B](xs: List[Int]): Option[Int] = Some(1)
}
def isFun(d: List[_]) = d match {
case Fun(f) => true
case _ => false
}
//> isFun: (d: List[_])Boolean
isFun(List(1.3))
//> res0: Boolean = true
The generic type that we wanted to pattern match against was Int, but it did work with Float.
So, I think that you should change your approach as this pattern matching is obviously going to be a problem.

Related

Flattening a List[Any] in scala using flatMap [duplicate]

It's a sad fact of life on Scala that if you instantiate a List[Int], you can verify that your instance is a List, and you can verify that any individual element of it is an Int, but not that it is a List[Int], as can be easily verified:
scala> List(1,2,3) match {
| case l : List[String] => println("A list of strings?!")
| case _ => println("Ok")
| }
warning: there were unchecked warnings; re-run with -unchecked for details
A list of strings?!
The -unchecked option puts the blame squarely on type erasure:
scala> List(1,2,3) match {
| case l : List[String] => println("A list of strings?!")
| case _ => println("Ok")
| }
<console>:6: warning: non variable type-argument String in type pattern is unchecked since it is eliminated by erasure
case l : List[String] => println("A list of strings?!")
^
A list of strings?!
Why is that, and how do I get around it?
This answer uses the Manifest-API, which is deprecated as of Scala 2.10. Please see answers below for more current solutions.
Scala was defined with Type Erasure because the Java Virtual Machine (JVM), unlike Java, did not get generics. This means that, at run time, only the class exists, not its type parameters. In the example, JVM knows it is handling a scala.collection.immutable.List, but not that this list is parameterized with Int.
Fortunately, there's a feature in Scala that lets you get around that. It’s the Manifest. A Manifest is class whose instances are objects representing types. Since these instances are objects, you can pass them around, store them, and generally call methods on them. With the support of implicit parameters, it becomes a very powerful tool. Take the following example, for instance:
object Registry {
import scala.reflect.Manifest
private var map= Map.empty[Any,(Manifest[_], Any)]
def register[T](name: Any, item: T)(implicit m: Manifest[T]) {
map = map.updated(name, m -> item)
}
def get[T](key:Any)(implicit m : Manifest[T]): Option[T] = {
map get key flatMap {
case (om, s) => if (om <:< m) Some(s.asInstanceOf[T]) else None
}
}
}
scala> Registry.register("a", List(1,2,3))
scala> Registry.get[List[Int]]("a")
res6: Option[List[Int]] = Some(List(1, 2, 3))
scala> Registry.get[List[String]]("a")
res7: Option[List[String]] = None
When storing an element, we store a "Manifest" of it too. A Manifest is a class whose instances represent Scala types. These objects have more information than JVM does, which enable us to test for the full, parameterized type.
Note, however, that a Manifest is still an evolving feature. As an example of its limitations, it presently doesn't know anything about variance, and assumes everything is co-variant. I expect it will get more stable and solid once the Scala reflection library, presently under development, gets finished.
You can do this using TypeTags (as Daniel already mentions, but I'll just spell it out explicitly):
import scala.reflect.runtime.universe._
def matchList[A: TypeTag](list: List[A]) = list match {
case strlist: List[String #unchecked] if typeOf[A] =:= typeOf[String] => println("A list of strings!")
case intlist: List[Int #unchecked] if typeOf[A] =:= typeOf[Int] => println("A list of ints!")
}
You can also do this using ClassTags (which saves you from having to depend on scala-reflect):
import scala.reflect.{ClassTag, classTag}
def matchList2[A : ClassTag](list: List[A]) = list match {
case strlist: List[String #unchecked] if classTag[A] == classTag[String] => println("A List of strings!")
case intlist: List[Int #unchecked] if classTag[A] == classTag[Int] => println("A list of ints!")
}
ClassTags can be used so long as you don't expect the type parameter A to itself be a generic type.
Unfortunately it's a little verbose and you need the #unchecked annotation to suppress a compiler warning. The TypeTag may be incorporated into the pattern match automatically by the compiler in the future: https://issues.scala-lang.org/browse/SI-6517
You can use the Typeable type class from shapeless to get the result you're after,
Sample REPL session,
scala> import shapeless.syntax.typeable._
import shapeless.syntax.typeable._
scala> val l1 : Any = List(1,2,3)
l1: Any = List(1, 2, 3)
scala> l1.cast[List[String]]
res0: Option[List[String]] = None
scala> l1.cast[List[Int]]
res1: Option[List[Int]] = Some(List(1, 2, 3))
The cast operation will be as precise wrt erasure as possible given the in-scope Typeable instances available.
I came up with a relatively simple solution that would suffice in limited-use situations, essentially wrapping parameterized types that would suffer from the type erasure problem in wrapper classes that can be used in a match statement.
case class StringListHolder(list:List[String])
StringListHolder(List("str1","str2")) match {
case holder: StringListHolder => holder.list foreach println
}
This has the expected output and limits the contents of our case class to the desired type, String Lists.
More details here: http://www.scalafied.com/?p=60
There is a way to overcome the type erasure issue in Scala. In Overcoming Type Erasure in matching 1 and Overcoming Type Erasure in Matching 2 (Variance) are some explanation of how to code some helpers to wrap the types, including Variance, for matching.
I found a slightly better workaround for this limitation of the otherwise awesome language.
In Scala, the issue of type erasure does not occur with arrays. I think it is easier to demonstrate this with an example.
Let us say we have a list of (Int, String), then the following gives a type erasure warning
x match {
case l:List[(Int, String)] =>
...
}
To work around this, first create a case class:
case class IntString(i:Int, s:String)
then in the pattern matching do something like:
x match {
case a:Array[IntString] =>
...
}
which seems to work perfectly.
This will require minor changes in your code to work with arrays instead of lists, but should not be a major problem.
Note that using case a:Array[(Int, String)] will still give a type erasure warning, so it is necessary to use a new container class (in this example, IntString).
Since Java does not know the actual element type, I found it most useful to just use List[_]. Then the warning goes away and the code describes reality - it is a list of something unknown.
I'm wondering if this is a suited workaround:
scala> List(1,2,3) match {
| case List(_: String, _*) => println("A list of strings?!")
| case _ => println("Ok")
| }
It does not match the "empty list" case, but it gives a compile error, not a warning!
error: type mismatch;
found: String
requirerd: Int
This on the other hand seems to work....
scala> List(1,2,3) match {
| case List(_: Int, _*) => println("A list of ints")
| case _ => println("Ok")
| }
Isn't it kinda even better or am I missing the point here?
Not a solution but a way to live with it without sweeping it under the rug altogether:
Adding the #unchecked annotation. See here - http://www.scala-lang.org/api/current/index.html#scala.unchecked
I wanted to add an answer which generalises the problem to: How do a get a String representation of the type of my list at runtime
import scala.reflect.runtime.universe._
def whatListAmI[A : TypeTag](list : List[A]) = {
if (typeTag[A] == typeTag[java.lang.String]) // note that typeTag[String] does not match due to type alias being a different type
println("its a String")
else if (typeTag[A] == typeTag[Int])
println("its a Int")
s"A List of ${typeTag[A].tpe.toString}"
}
val listInt = List(1,2,3)
val listString = List("a", "b", "c")
println(whatListAmI(listInt))
println(whatListAmI(listString))
Using pattern match guard
list match {
case x:List if x.isInstanceOf(List[String]) => do sth
case x:List if x.isInstanceOf(List[Int]) => do sth else
}

How to suppress the "match may not be exhaustive" warning in Scala? [duplicate]

This question already has answers here:
How to suppress "match is not exhaustive!" warning in Scala
(4 answers)
Closed 5 years ago.
I am currently implementing a small internally used utility that imports data from a set of Excel files to our application. Based on the type of the Excel cell, I decide how to treat the data. The Excel file can contain only string, numeric and Boolean cells, if any other cell is encountered, the program is free to crash at run-time.
Now Scala is correctly warning me that the match on the cell type is not exhaustive, the cells can have the type of blank, formula, error...
Is there a way how to suppress the warning, except the obvious one?:
t match {
case STRING => ???
case NUMERIC => ???
case BOOLEAN => ???
case _ => throw new MatchError() // redundant, just to suppress the compile time warning
}
You could use the unchecked annotation:
(t: #unchecked) match {
case STRING => ???
case NUMERIC => ???
case BOOLEAN => ???
}
As others have mentioned, you should avoid using it and instead choose your types accordingly to avoid such warnings (e.g. by using sealed traits). If, however, you find yourself unable to do so, and you have some kind of invariant that guarantees that your type is always one of the given types, using #unchecked solves your problem.
This is not recommended (for usual cases), but if you really need it, use PartialFuncion:
sealed trait Trait
case object A extends Trait
case object B extends Trait
scala> def f(x: Trait) = x match {
| case A => println("A")
| }
<console>:13: warning: match may not be exhaustive.
It would fail on the following input: B
def f(x: Trait) = x match {
^
f: (x: Trait)Unit
scala> val f: PartialFunction[Trait, Unit] = {
| case A => println("A")
| }
f: PartialFunction[Trait,Unit] = <function1>
scala> f(A)
A
scala> f(B)
scala.MatchError: B (of class B$)
at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:254)
at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:252)
at $anonfun$1.applyOrElse(<console>:14)
at $anonfun$1.applyOrElse(<console>:13)
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:34)
... 29 elided
The disadvantage of partial functions is that they create a risk of runtime exception. But they're more safe than #unchecked approach (should be used in extreme cases) - at least user can see potential exception from the type itself.
In your case I'd recommend you to use Option and separate logic from effects:
def f(x: Trait) = x match {
case A => Some("A")
case _ => None
}
def effect(x: Option[String]) = x.foreach(println) //you can use getOrElse(throw RuntimeException) here
def run(x: Trait) = effect(f(x))
scala> run(A)
A
scala> run(B)

Scala case match type erasure warning [duplicate]

It's a sad fact of life on Scala that if you instantiate a List[Int], you can verify that your instance is a List, and you can verify that any individual element of it is an Int, but not that it is a List[Int], as can be easily verified:
scala> List(1,2,3) match {
| case l : List[String] => println("A list of strings?!")
| case _ => println("Ok")
| }
warning: there were unchecked warnings; re-run with -unchecked for details
A list of strings?!
The -unchecked option puts the blame squarely on type erasure:
scala> List(1,2,3) match {
| case l : List[String] => println("A list of strings?!")
| case _ => println("Ok")
| }
<console>:6: warning: non variable type-argument String in type pattern is unchecked since it is eliminated by erasure
case l : List[String] => println("A list of strings?!")
^
A list of strings?!
Why is that, and how do I get around it?
This answer uses the Manifest-API, which is deprecated as of Scala 2.10. Please see answers below for more current solutions.
Scala was defined with Type Erasure because the Java Virtual Machine (JVM), unlike Java, did not get generics. This means that, at run time, only the class exists, not its type parameters. In the example, JVM knows it is handling a scala.collection.immutable.List, but not that this list is parameterized with Int.
Fortunately, there's a feature in Scala that lets you get around that. It’s the Manifest. A Manifest is class whose instances are objects representing types. Since these instances are objects, you can pass them around, store them, and generally call methods on them. With the support of implicit parameters, it becomes a very powerful tool. Take the following example, for instance:
object Registry {
import scala.reflect.Manifest
private var map= Map.empty[Any,(Manifest[_], Any)]
def register[T](name: Any, item: T)(implicit m: Manifest[T]) {
map = map.updated(name, m -> item)
}
def get[T](key:Any)(implicit m : Manifest[T]): Option[T] = {
map get key flatMap {
case (om, s) => if (om <:< m) Some(s.asInstanceOf[T]) else None
}
}
}
scala> Registry.register("a", List(1,2,3))
scala> Registry.get[List[Int]]("a")
res6: Option[List[Int]] = Some(List(1, 2, 3))
scala> Registry.get[List[String]]("a")
res7: Option[List[String]] = None
When storing an element, we store a "Manifest" of it too. A Manifest is a class whose instances represent Scala types. These objects have more information than JVM does, which enable us to test for the full, parameterized type.
Note, however, that a Manifest is still an evolving feature. As an example of its limitations, it presently doesn't know anything about variance, and assumes everything is co-variant. I expect it will get more stable and solid once the Scala reflection library, presently under development, gets finished.
You can do this using TypeTags (as Daniel already mentions, but I'll just spell it out explicitly):
import scala.reflect.runtime.universe._
def matchList[A: TypeTag](list: List[A]) = list match {
case strlist: List[String #unchecked] if typeOf[A] =:= typeOf[String] => println("A list of strings!")
case intlist: List[Int #unchecked] if typeOf[A] =:= typeOf[Int] => println("A list of ints!")
}
You can also do this using ClassTags (which saves you from having to depend on scala-reflect):
import scala.reflect.{ClassTag, classTag}
def matchList2[A : ClassTag](list: List[A]) = list match {
case strlist: List[String #unchecked] if classTag[A] == classTag[String] => println("A List of strings!")
case intlist: List[Int #unchecked] if classTag[A] == classTag[Int] => println("A list of ints!")
}
ClassTags can be used so long as you don't expect the type parameter A to itself be a generic type.
Unfortunately it's a little verbose and you need the #unchecked annotation to suppress a compiler warning. The TypeTag may be incorporated into the pattern match automatically by the compiler in the future: https://issues.scala-lang.org/browse/SI-6517
You can use the Typeable type class from shapeless to get the result you're after,
Sample REPL session,
scala> import shapeless.syntax.typeable._
import shapeless.syntax.typeable._
scala> val l1 : Any = List(1,2,3)
l1: Any = List(1, 2, 3)
scala> l1.cast[List[String]]
res0: Option[List[String]] = None
scala> l1.cast[List[Int]]
res1: Option[List[Int]] = Some(List(1, 2, 3))
The cast operation will be as precise wrt erasure as possible given the in-scope Typeable instances available.
I came up with a relatively simple solution that would suffice in limited-use situations, essentially wrapping parameterized types that would suffer from the type erasure problem in wrapper classes that can be used in a match statement.
case class StringListHolder(list:List[String])
StringListHolder(List("str1","str2")) match {
case holder: StringListHolder => holder.list foreach println
}
This has the expected output and limits the contents of our case class to the desired type, String Lists.
More details here: http://www.scalafied.com/?p=60
There is a way to overcome the type erasure issue in Scala. In Overcoming Type Erasure in matching 1 and Overcoming Type Erasure in Matching 2 (Variance) are some explanation of how to code some helpers to wrap the types, including Variance, for matching.
I found a slightly better workaround for this limitation of the otherwise awesome language.
In Scala, the issue of type erasure does not occur with arrays. I think it is easier to demonstrate this with an example.
Let us say we have a list of (Int, String), then the following gives a type erasure warning
x match {
case l:List[(Int, String)] =>
...
}
To work around this, first create a case class:
case class IntString(i:Int, s:String)
then in the pattern matching do something like:
x match {
case a:Array[IntString] =>
...
}
which seems to work perfectly.
This will require minor changes in your code to work with arrays instead of lists, but should not be a major problem.
Note that using case a:Array[(Int, String)] will still give a type erasure warning, so it is necessary to use a new container class (in this example, IntString).
Since Java does not know the actual element type, I found it most useful to just use List[_]. Then the warning goes away and the code describes reality - it is a list of something unknown.
I'm wondering if this is a suited workaround:
scala> List(1,2,3) match {
| case List(_: String, _*) => println("A list of strings?!")
| case _ => println("Ok")
| }
It does not match the "empty list" case, but it gives a compile error, not a warning!
error: type mismatch;
found: String
requirerd: Int
This on the other hand seems to work....
scala> List(1,2,3) match {
| case List(_: Int, _*) => println("A list of ints")
| case _ => println("Ok")
| }
Isn't it kinda even better or am I missing the point here?
Not a solution but a way to live with it without sweeping it under the rug altogether:
Adding the #unchecked annotation. See here - http://www.scala-lang.org/api/current/index.html#scala.unchecked
I wanted to add an answer which generalises the problem to: How do a get a String representation of the type of my list at runtime
import scala.reflect.runtime.universe._
def whatListAmI[A : TypeTag](list : List[A]) = {
if (typeTag[A] == typeTag[java.lang.String]) // note that typeTag[String] does not match due to type alias being a different type
println("its a String")
else if (typeTag[A] == typeTag[Int])
println("its a Int")
s"A List of ${typeTag[A].tpe.toString}"
}
val listInt = List(1,2,3)
val listString = List("a", "b", "c")
println(whatListAmI(listInt))
println(whatListAmI(listString))
Using pattern match guard
list match {
case x:List if x.isInstanceOf(List[String]) => do sth
case x:List if x.isInstanceOf(List[Int]) => do sth else
}

Scala pattern matching and type inference

Could someone explain why the following code compiles?
Option("foo") match {
case x: List[String] => println("A")
case _ => println("B")
}
This gives me an (expected) warning about type erasure, but it still compiles. I expected this to throw a type error, like it does if I matched on "foo" instead of Option("foo").
Thanks!
The code is commented, so let's take a moment to savor that:
/** If we can absolutely rule out a match we can fail early.
* This is the case if the scrutinee has no unresolved type arguments
* and is a "final type", meaning final + invariant in all type parameters.
*/
Notice that None is not final, for instance. I know, right?
If you ever try scalac -Ypatmat-debug, the comment here might help:
https://github.com/scala/scala/pull/650
Reachability is almost within reach:
https://issues.scala-lang.org/browse/SI-6146
But I don't see any promises about what might someday be warnable. For performance reasons? One could also say, why should it warn about an instanceOf[Foo[_]]?
For now, the spec sections 8.2 - 8.4 motivate why matching against Foo[a] is interesting (because of the bounds a acquires). I think I'll go read that again. After some coffee.
trait Foo[+A]
final class Fuzz[+A] extends Foo[A]
final object Fooz extends Foo[Nothing]
object Futz extends Foo[Nothing]
//error
Fooz match {
case x: List[_] => println("A")
case _ => println("B")
}
//no error
Futz match { ... }
I would assume that the compiler is treating both Option and List as Product, which is why it compiles. As you say, the warning about type erasure is expected. Here's an example that uses another Product:
scala> Option("foo") match {
| case x: Tuple2[String,String] => println("TUPLE")
| case x: List[String] => println("LIST")
| case _ => println("OTHER")
| }
<console>:9: warning: non variable type-argument String in type pattern (String, String) is unchecked since it is eliminated by erasure
case x: Tuple2[String,String] => println("TUPLE")
^
<console>:10: warning: non variable type-argument String in type pattern List[String] is unchecked since it is eliminated by erasure
case x: List[String] => println("LIST")
^
UPDATE w/r/t case classes (because of the comment below):
scala> case class Foo(bar: Int)
defined class Foo
scala> val y: Product = Foo(123)
y: Product = Foo(123)
I noticed that an error is shown when the class of the value you match is declared as final (and we know that String is final). I still don't know why there's no error without it.

How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections?

It's a sad fact of life on Scala that if you instantiate a List[Int], you can verify that your instance is a List, and you can verify that any individual element of it is an Int, but not that it is a List[Int], as can be easily verified:
scala> List(1,2,3) match {
| case l : List[String] => println("A list of strings?!")
| case _ => println("Ok")
| }
warning: there were unchecked warnings; re-run with -unchecked for details
A list of strings?!
The -unchecked option puts the blame squarely on type erasure:
scala> List(1,2,3) match {
| case l : List[String] => println("A list of strings?!")
| case _ => println("Ok")
| }
<console>:6: warning: non variable type-argument String in type pattern is unchecked since it is eliminated by erasure
case l : List[String] => println("A list of strings?!")
^
A list of strings?!
Why is that, and how do I get around it?
This answer uses the Manifest-API, which is deprecated as of Scala 2.10. Please see answers below for more current solutions.
Scala was defined with Type Erasure because the Java Virtual Machine (JVM), unlike Java, did not get generics. This means that, at run time, only the class exists, not its type parameters. In the example, JVM knows it is handling a scala.collection.immutable.List, but not that this list is parameterized with Int.
Fortunately, there's a feature in Scala that lets you get around that. It’s the Manifest. A Manifest is class whose instances are objects representing types. Since these instances are objects, you can pass them around, store them, and generally call methods on them. With the support of implicit parameters, it becomes a very powerful tool. Take the following example, for instance:
object Registry {
import scala.reflect.Manifest
private var map= Map.empty[Any,(Manifest[_], Any)]
def register[T](name: Any, item: T)(implicit m: Manifest[T]) {
map = map.updated(name, m -> item)
}
def get[T](key:Any)(implicit m : Manifest[T]): Option[T] = {
map get key flatMap {
case (om, s) => if (om <:< m) Some(s.asInstanceOf[T]) else None
}
}
}
scala> Registry.register("a", List(1,2,3))
scala> Registry.get[List[Int]]("a")
res6: Option[List[Int]] = Some(List(1, 2, 3))
scala> Registry.get[List[String]]("a")
res7: Option[List[String]] = None
When storing an element, we store a "Manifest" of it too. A Manifest is a class whose instances represent Scala types. These objects have more information than JVM does, which enable us to test for the full, parameterized type.
Note, however, that a Manifest is still an evolving feature. As an example of its limitations, it presently doesn't know anything about variance, and assumes everything is co-variant. I expect it will get more stable and solid once the Scala reflection library, presently under development, gets finished.
You can do this using TypeTags (as Daniel already mentions, but I'll just spell it out explicitly):
import scala.reflect.runtime.universe._
def matchList[A: TypeTag](list: List[A]) = list match {
case strlist: List[String #unchecked] if typeOf[A] =:= typeOf[String] => println("A list of strings!")
case intlist: List[Int #unchecked] if typeOf[A] =:= typeOf[Int] => println("A list of ints!")
}
You can also do this using ClassTags (which saves you from having to depend on scala-reflect):
import scala.reflect.{ClassTag, classTag}
def matchList2[A : ClassTag](list: List[A]) = list match {
case strlist: List[String #unchecked] if classTag[A] == classTag[String] => println("A List of strings!")
case intlist: List[Int #unchecked] if classTag[A] == classTag[Int] => println("A list of ints!")
}
ClassTags can be used so long as you don't expect the type parameter A to itself be a generic type.
Unfortunately it's a little verbose and you need the #unchecked annotation to suppress a compiler warning. The TypeTag may be incorporated into the pattern match automatically by the compiler in the future: https://issues.scala-lang.org/browse/SI-6517
You can use the Typeable type class from shapeless to get the result you're after,
Sample REPL session,
scala> import shapeless.syntax.typeable._
import shapeless.syntax.typeable._
scala> val l1 : Any = List(1,2,3)
l1: Any = List(1, 2, 3)
scala> l1.cast[List[String]]
res0: Option[List[String]] = None
scala> l1.cast[List[Int]]
res1: Option[List[Int]] = Some(List(1, 2, 3))
The cast operation will be as precise wrt erasure as possible given the in-scope Typeable instances available.
I came up with a relatively simple solution that would suffice in limited-use situations, essentially wrapping parameterized types that would suffer from the type erasure problem in wrapper classes that can be used in a match statement.
case class StringListHolder(list:List[String])
StringListHolder(List("str1","str2")) match {
case holder: StringListHolder => holder.list foreach println
}
This has the expected output and limits the contents of our case class to the desired type, String Lists.
More details here: http://www.scalafied.com/?p=60
There is a way to overcome the type erasure issue in Scala. In Overcoming Type Erasure in matching 1 and Overcoming Type Erasure in Matching 2 (Variance) are some explanation of how to code some helpers to wrap the types, including Variance, for matching.
I found a slightly better workaround for this limitation of the otherwise awesome language.
In Scala, the issue of type erasure does not occur with arrays. I think it is easier to demonstrate this with an example.
Let us say we have a list of (Int, String), then the following gives a type erasure warning
x match {
case l:List[(Int, String)] =>
...
}
To work around this, first create a case class:
case class IntString(i:Int, s:String)
then in the pattern matching do something like:
x match {
case a:Array[IntString] =>
...
}
which seems to work perfectly.
This will require minor changes in your code to work with arrays instead of lists, but should not be a major problem.
Note that using case a:Array[(Int, String)] will still give a type erasure warning, so it is necessary to use a new container class (in this example, IntString).
Since Java does not know the actual element type, I found it most useful to just use List[_]. Then the warning goes away and the code describes reality - it is a list of something unknown.
I'm wondering if this is a suited workaround:
scala> List(1,2,3) match {
| case List(_: String, _*) => println("A list of strings?!")
| case _ => println("Ok")
| }
It does not match the "empty list" case, but it gives a compile error, not a warning!
error: type mismatch;
found: String
requirerd: Int
This on the other hand seems to work....
scala> List(1,2,3) match {
| case List(_: Int, _*) => println("A list of ints")
| case _ => println("Ok")
| }
Isn't it kinda even better or am I missing the point here?
Not a solution but a way to live with it without sweeping it under the rug altogether:
Adding the #unchecked annotation. See here - http://www.scala-lang.org/api/current/index.html#scala.unchecked
I wanted to add an answer which generalises the problem to: How do a get a String representation of the type of my list at runtime
import scala.reflect.runtime.universe._
def whatListAmI[A : TypeTag](list : List[A]) = {
if (typeTag[A] == typeTag[java.lang.String]) // note that typeTag[String] does not match due to type alias being a different type
println("its a String")
else if (typeTag[A] == typeTag[Int])
println("its a Int")
s"A List of ${typeTag[A].tpe.toString}"
}
val listInt = List(1,2,3)
val listString = List("a", "b", "c")
println(whatListAmI(listInt))
println(whatListAmI(listString))
Using pattern match guard
list match {
case x:List if x.isInstanceOf(List[String]) => do sth
case x:List if x.isInstanceOf(List[Int]) => do sth else
}