Pattern matching a generic class [duplicate] - scala

This question already has answers here:
How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections?
(11 answers)
Closed 2 years ago.
Why does the following piece of code not work as expected? By looking at the code, I was thinking that it might return "a list of numbers" as numbers is a list with numbers. but I ran the code and got the output as "list of strings". Guess I cleared most of the confusion around here.
val numbers: List[Int] = List(1,2,3)
val numbersMatch: String = numbers match {
case listOfStrings: List[String] => "a list of strings"
case listOfNumbers: List[Int] => "a list of numbers"
case _ => ""
}
println(numbersMatch)

This code will print that it is a list of string which is a result of the way generics are compiled on most JVM languages.
They use so called "Type erasure" which drops generics. Since the generic type is not available at runtime, Scala cannot differentiate between List[String] and List[Int] and therefore the first case matches. If you compile your code, Scala will actually warn you about this:
warning: fruitless type test: a value of type List[Int] cannot also be a List[String] (the underlying of List[String]) (but still might match its erasure)
case listOfStrings: List[String] => "a list of strings"

Related

Scala - Iterate a Any variable which contains List()

scala >
var a : Any = List(1,2,3,4,5,6,7,8,9,0)
I want to iterate variable a. as it print
1
2
3
4
5
6
7
8
9
0
Collections such as List are usually "iterated" over using map/foreach higher-order methods, however the reason we cannot call them directly on a is because compiler thinks the type of a is Any as we explicitly specified type annotation a: Any.
var a: Any = List(1,2,3,4,5,6,7,8,9,0)
| |
compile-time type runtime class
Any does not provide map/foreach API so the best we can do is it to perform a runtime cast of reference a to class List[_] like so
if (a.isInstanceOf[List[_]]) a.asInstanceOf[List[_]].foreach(println) else ???
which can be equivalently sugared using pattern match
a match {
case value: List[_] => value.foreach(println)
case _ => ???
}
As a side-note, due to type erasure we can only check the "top-level" class List and not, for example, List[Int], hence a.isInstanceOf[List[Int]] is a bit misleading so I prefer expressing it a.isInstanceOf[List[_]].
Just use pattern matching:
a match {
case l: List[Int] => l.foreach(println)
}
P.S.: As #IvanStanislavciuc cleverly notices, there is a warning:
warning: non-variable type argument Int in type pattern List[Int] (the underlying of List[Int]) is unchecked since it is eliminated by erasure
1
It's because of type erasure, but List needs a type parameter, so you can as well pass Any instead of Int.

Scala :How to differentiate list from other data types when using Any [duplicate]

This question already has answers here:
How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections?
(11 answers)
sbt compile time warning: non-variable type argument String in type pattern List[String]
(2 answers)
Closed 3 years ago.
I have a function which takes the parameter. Its data type is Any. I want to differentiate List from other data types like String, Int, Double. I want to check if the parameter value is list or not here is my code
val userList: List[String] = List("1","2")
val user = "UserList"
val name = "Name"
myFunction(user, userList)
def myFunction(fieldName: String, value: Any): Unit = {
if (fieldName.equals(user)) {
value match {
case list: List[String] => print("its a list perform operation accordingly")
}
}
else {
log.info("its a string")
}
}
But the compiler generates the warning:
non-variable type argument String in type pattern List[String] (the
underlying of List[String]) is unchecked since it is eliminated by
erasure
I want to fix this warning message.
I do not want to use asInstanceOf like
val list = value.asInstanceOf[List[String]]
to avoid this warning message.
Since you don't care about the element type of the List you can just leave the type blank:
case _: List[_] => print("its a list perform operation accordingly")
You cannot test specifically for List[String] because this information is not available when the test is executed. It you want to do this you will need to use a type class or polymorphism.

Obscure Scala: what does following mean in scala? ( : _* ) [duplicate]

This question already has answers here:
What does param: _* mean in Scala?
(3 answers)
Closed 8 years ago.
Hi I have following code
CookieMock(response, email).cookies: _*
.cookies is type def cookies: scala.Seq[Cookie].
What does :_* mean in Scala?
Thanks
If you are familiar with Java
here is the same explanation in Java:
varargs
Because * is not a type, you add the underscore.
def printInts(ints: Int*) = ints.mkString(",")
printInts(1,2,3)
//printInts(List(1,2,3)) //type mismatch; found : List[Int] required: Int
printInts(List(1,2,3): _*)
paste this to codebrew.io this will clarify.
The : is type ascription. _* is the type you ascribe when you need a Seq[A] to be treated as A*.
http://docs.scala-lang.org/style/types.html
The following are examples of ascription:
Nil: List[String]
Set(values: _*)
"Daniel": AnyRef
Ascription is basically just an up-cast performed at compile-time for the sake of the type checker. Its use is not common, but it does happen on occasion. The most often seen case of ascription is invoking a varargs method with a single Seq parameter. This is done by ascribing the _* type (as in the second example above).

Scala compile warning with pattern matching [duplicate]

This question already has answers here:
How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections?
(11 answers)
Closed 8 years ago.
I have an Akka Actor that has the following case pattern match check in its receive method as below:
def receive = {
case x: (String, ListBuffer[String]) if(x._2.size >= 0) => {
.....
.....
}
When I compile, I get to see the following compiler warnings:
warning: non-variable type argument String in type pattern (String, scala.collection.mutable.ListBuffer[String])
is unchecked since it is eliminated by erasure)
Any clues as to how I could get rid of them? I do not want to set the compiler settings to ignore these warnings, but I do not see a reason why the compiler issues a warning?
This is due to the JVM's type erasure. At runtime, the JVM only sees ListBuffer[Any]. Static type information of generics is lost. If you don't care about the generic type of the ListBuffer, you can change the pattern match to:
case x: (String, ListBuffer[_]) if(x._2.size >= 0) =>
One little trick I like to use for this problem is type aliasing.
type MyBuffer = ListBuffer[String]
//...
def receive = {
case x: (String, MyBuffer) if(x._2.size >= 0) => {
//.....
//.....
}

How does generics work in scala REPL?

I knew what type erasure is. so, i think scala REPL cannot detect a generic type exactly.
As i mentioned above, scala can't detect generic type in pattern matching like this:
case list: List[Int]
But when i declare List type value, scala detects what generic type is contained.
scala> val a = List(1,2,3)
a: List[Int] = List(1, 2, 3)
How is it possible?
val a = List(1,2,3)
This is equivalent to:
val a = List.apply[Int](1,2,3)
The result type of List.apply[Int](...) is List[Int] and therefore, type inferencer assigns this type to identifier a. This happens during compilation. The REPL does not "detect" the type in runtime.
This is different from a pattern match:
val a: Any = ...
a match {
case list: List[Int] => ...
}
Here, we have a value a, for which we don't have any type information. So we're trying to check what type it is, but now we're doing this in runtime. And here we indeed cannot determine the exact type. The best we can do here is to match against List[_].
Summarizing:
When you type some code in the REPL, it first gets compiled into bytecode and then, evaluated. Displayed type information comes from the compilation phase, so it doesn't suffer from type erasure.
When you write:
val a = List(1,2,3)
Scala uses type inference to find the closest matching type during compile time. Essentially it will rewrite it for you as:
val a: List[Int] = ...
It will use this parameter type information for compile time to type check your code and erase it afterwards so you'll get List[_] in your program. This is because JVM works this way - type erasure.
When you pattern match on a list during runtime it's type information is erased so any List would match. Scala compiler will warn you during compilation about it.
This works in the same way in REPL and in regular compile->run cycle.