Scala - type erasure? [duplicate] - scala

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections?
I ran the following code:
scala> var s = new Stack()push(1)
s: scalatest.Stack[Int] = 1
scala> s match { case s : Stack[String] => print("Hello")}
<console>:12: warning: non variable type-argument String in type pattern scalatest.Stack[String] is unchecked since it is eliminated by erasure
s match { case s : Stack[String] => print("Hello")
}
Stack is the class taken from http://www.scala-lang.org/node/129. If I run this code without -unchecked flag it will print "Hello". Why is that the case?

The problem is that you're matching s to be of type Stack[String]. During runtime, it's possible to determine if s is of type Stack, but because of Java's type erasure it's not possible to determine if s is of type Stack[String], Stack[Int] etc. So no matter what the type parameter was, it gets matched by the case expression. This is why Scala issues the warning. It is the same as if you match as
s match { case s : Stack[_] => print("Hello")}
(which will compile without warnings).
Edit: A workaround (for Java too) is to create a specific class that doesn't have type parameters any more. For example:
import scala.collection.mutable.Stack;
object Test extends App {
class MyStack extends Stack[Int];
class MyOtherStack extends Stack[String];
val s: Stack[_] = new MyStack().push(1);
s match {
case s : MyOtherStack => print("Hello String");
case s : MyStack => print("Hello Int");
}
}
It has a drawback that you cannot use it for immutable containers, because their methods create new objects and they won't be instances of these our specific subclasses.

Related

Scala pattern matching not working with Option[Seq[String]] [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 3 months ago.
I am new to Scala(2.13.8) and working on code to use pattern matching to handle a value in different ways, code is very simply like below
def getOption(o: Option[Any]): Unit = {
o match {
case l: Some[List[String]] => handleListData(l)
case _ => handleData(_)
}
}
getOption(Some(3))
getOption(Some(Seq("5555")))
The result is handleListData() been invoked for both input. Can someone help on what's wrong in my code?
As sarveshseri mentioned in the comments, the problem here is caused by type erasure. When you compile this code, scalac issues a warning:
[warn] /Users/tmoore/IdeaProjects/scala-scratch/src/main/scala/PatternMatch.scala:6:15: non-variable type argument List[String] in type pattern Some[List[String]] is unchecked since it is eliminated by erasure
[warn] case l: Some[List[String]] => handleListData(l)
[warn] ^
This is because the values of type parameters are not available at runtime due to erasure, so this case is equivalent to:
case l: Some[_] => handleListData(l.asInstanceOf[Some[List[String]]])
This may fail at runtime due to an automatically-inserted cast in handleListData, depending on how it actually uses its argument.
One thing you can do is take advantage of destructuring in the case pattern in order to do a runtime type check on the content of the Option:
case Some(l: List[_]) => handleListData(l)
This will work with a handleListData with a signature like this:
def handleListData(l: List[_]): Unit
Note that it unwraps the Option, which is most likely more useful than passing it along.
However, it does not check that the List contains strings. To do so would require inspecting each item in the list. The alternative is an unsafe cast, made with the assumption that the list contains strings. This opens up the possibility of runtime exceptions later if the list elements are cast to strings, and are in fact some other type.
This change also reveals a problem with the second case:
case _ => handleData(_)
This does not do what you probably think it does, and issues its own compiler warning:
warn] /Users/tmoore/IdeaProjects/scala-scratch/src/main/scala/PatternMatch.scala:7:28: a pure expression does nothing in statement position
[warn] case _ => handleData(_)
[warn] ^
What does this mean? It's telling us that this operation has no effect. It does not invoke the handleData method with o as you might think. This is because the _ character has special meaning in Scala, and that meaning depends on the context where it's used.
In the pattern match case _, it is a wildcard that means "match anything without binding the match to a variable". In the expression handleData(_) it is essentially shorthand for x => handleData(x). In other words, when this case is reached, it evaluates to a Function value that would invoke handleData when applied, and then discards that value without invoking it. The result is that any value of o that doesn't match the first case will have no effect, and handleData is never called.
This can be solved by using o in the call:
case _ => handleData(o)
or by assigning a name to the match:
case x => handleData(x)
Returning to the original problem: how can you call handleListData only when the argument contains a List[String]? Since the type parameter is erased at runtime, this requires some other kind of runtime type information to differentiate it. A common approach is to define a custom algebraic data type instead of using Option:
object PatternMatch {
sealed trait Data
case class StringListData(l: List[String]) extends Data
case class OtherData(o: Any) extends Data
def handle(o: Data): Unit = {
o match {
case StringListData(l) => handleListData(l)
case x => handleData(x)
}
}
def handleListData(l: List[String]): Unit = println(s"Handling string list data: $l")
def handleData(value: Any): Unit = println(s"Handling data: $value")
def main(args: Array[String]): Unit = {
PatternMatch.handle(OtherData(3))
PatternMatch.handle(StringListData(List("5555", "6666")))
PatternMatch.handle(OtherData(List(7777, 8888)))
PatternMatch.handle(OtherData(List("uh oh!")))
/*
* Output:
* Handling data: OtherData(3)
* Handling string list data: List(5555, 6666)
* Handling data: OtherData(List(7777, 8888))
* Handling data: OtherData(List(uh oh!))
*/
}
}
Note that it's still possible here to create an instance of OtherData that actually contains a List[String], in which case handleData is called instead of handleListData. You would need to be careful not to do this when creating the Data passed to handle. This is the best you can do if you really need to handle Any in the default case. You can also extend this pattern with other special cases by creating new subtypes of Data, including a case object to handle the "empty" case, if needed (similar to None for Option):
case object NoData extends Data
// ...
PatternMatch.handle(NoData) // prints: 'Handling data: NoData'

Scala how to fix type erasure warning even though I added ClassTag

I am wondering how to fix the type erasure warning even though I added ClassTag? is there an easy fix without adding an additional library like TypeTag?
import scala.reflect.ClassTag
class Node
class C_PHYLUM[T]
class I_PHYLUM[T](name: String)
trait C_SEQUENCE[T1,T2]
class T_SEQUENCE[T]
class M_SEQUENCE[T_ElemType <: Node : ClassTag](name : String, t_ElemType:C_PHYLUM[T_ElemType])
extends I_PHYLUM[T_SEQUENCE[T_ElemType]](name)
with C_SEQUENCE[T_SEQUENCE[T_ElemType],T_ElemType]
{
type T_Result = T_SEQUENCE[T_ElemType];
def u__op_AC(x:Any) : Option[(T_Result,Seq[T_ElemType])] = x match {
case x : T_Result => Some((x,toList(x)));
case _ => None
};
def toList[T](x: T): Seq[T_ElemType] = Seq(x.asInstanceOf[T_ElemType])
}
Warning:
warning: abstract type T_ElemType in type pattern T_SEQUENCE[T_ElemType] (the underlying of M_SEQUENCE.this.T_Result) is unchecked since it is eliminated by erasure
case x : T_Result => Some((x,toList(x)));
Re-producable example
Type erasure is a consequence of how generics are implemented on the JVM. It means that a runtime, you won't be able to tell the difference between e.g. a List[Int] and a List[String]; all the runtime knows is that you have a List. The knowledge of whether you're expecting [Int] or [String] is compile-time only.
In your case, you have T_SEQUENCE[T_ElemType] - so given some object of type Any, you can use isInstanceOf to check if you have an instance of some T_SEQUENCE, but there's no knowledge of what the type parameter on it is.
You could make an assumption that your method will never receive a T_SEQUENCE with the wrong type parameter, and use that to do
case x : T_SEQUENCE[_] => Some((x.asInstanceOf[T_Result],toList(x.asInstanceOf[T_Result])))
Or, assuming your T_SEQUENCE class has some kind of .map method that lets you transform the value inside, you could do something that delegates to the ClassTag that you brought in, e.g.
// I'll assume a method like this exists:
def transformTSeq[A, B](tSeq: T_SEQUENCE[A], f: A => Option[B]): Option[T_SEQUENCE[B]]
// inside your match
case x: T_SEQUENCE[_] =>
transformTSeq(x, value => {
implicitly[ClassTag[T_ElemType]].unapply(value)
})
case _ =>
None
P.S. get rid of those semicolons

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.

Scala + pattern matching + String autoboxing [duplicate]

This question already has answers here:
How to pattern match on generic type in Scala?
(4 answers)
Closed 6 years ago.
Am reporting a feature that sounds wierd and unable to reason the following behavior with Pattern matching in scala.
def typesPattern(x:Any)= x match{
case s:String⇒ s.length
case n:Map[Int,Int]⇒println("Map[Int,Int]");var a = n.iterator.next();println(a._1);println(a._2);n.size;
case n:Map[a,b]⇒println("Map[a,b]");n.size;
case m:Map[_,_]⇒ m.size
case _ ⇒ -1
}
}
When i invoke above function with following println(typesPattern(Map("a"→10))) i get following error Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:101)
at scala.Tuple2._1$mcI$sp(Tuple2.scala:20)
First question that I have is "WHY MAP[String->Int] is getting matched with MAP[INT,INT]?", it should have rather matched with MAP[_,_].
Interestingly when I edit the pattern matching code and take out the code that extracts tuple from Map and prints key and value pair
`def typesPattern(x:Any)= x match{
case s:String⇒ s.length
case n:Map[Int,Int]⇒println("Map[Int,Int]");n.size;
case n:Map[a,b]⇒println("Map[a,b]");n.size;
case m:Map[_,_]⇒ m.size
case _ ⇒ -1
}
}`
Now the same invocation like earlier println(typesPattern(Map("a"→10))) does match MAP[INT,INT] without issues and prints the size.
Map[Int,Int]
1
Second question
"WHY THIS TIME SCALA IS ABLE TO MATCH MAP[String->INT] with MAP[INT->INT] (which i still wonder how?) without issues?
You maybe tried to look at warnings compiler gives you?
<console>:12: warning: non-variable type argument Int in type pattern scala.collection.immutable.Map[Int,Int] (the underlying of Map[Int,Int]) is unchecked since it is eliminated by erasure
case n:Map[Int,Int]⇒println("Map[Int,Int]");var a = n.iterator.next();println(a._1);println(a._2);n.size;
^
<console>:13: warning: unreachable code
case n:Map[a,b]⇒println("Map[a,b]");n.size;
Actually, both of these lines:
case n:Map[a,b]⇒println("Map[a,b]");n.size;
case m:Map[_,_]⇒ m.size
are unreachable, because all three lines that match on map are equivalent, at least their pattern will match the same things.
In runtime there is no generic types, they are erased, so Map[A, B] is just a Map. So your only case matching the map is the first one, as they are tested in order
case n:Map[Int,Int]⇒println("Map[Int,Int]");var a = n.iterator.next();println(a._1);println(a._2);n.size;
You get a ClassCastException only when you try to use the values treating them like an Int, because they get cast only if you try to use them. Checking size does not depend on type of its values.
This problem happens because of generics type erasure. In runtime there is no difference between Map of any types. That's why pattern matches at first suitable case.
Simple snippet to check it:
List[String]().isInstanceOf[List[String]] // true
List[String]().isInstanceOf[List[Integer]] // true
It's because of the type erasure. Usage of generic types is of no use in case clauses as it does not retain type information. So MAP[String->Int] is equivalent to Map. That is why MAP[String->Int] match with MAP[Int->Int].
Wouldn't much easier if instead of trying to use pattern matching, you use instead implicits and type classes mechanism?
trait TypePattern[A,B] {
def pattern(a: A):B
}
implicit object stringPattern extends TypePattern[String,Int] {
override def pattern(a: String): Int = a.length
}
implicit object mapIntIntPattern extends TypePattern[Map[Int, Int],Int] {
override def pattern(n: Map[Int, Int]): Int = {
println("Map[Int,Int]")
var a = n.iterator.next()
println(a._1)
println(a._2)
n.size
}
}
implicit object mapAnyPattern extends TypePattern[Map[Any, Any],Int] {
override def pattern(a: Map[Any, Any]): Int = {
println("Map[a,b]")
a.size
}
}
def pattern[A,B](x: A)(implicit typePattern: TypePattern[A,B]): B = {
typePattern.pattern(x)
}

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) => {
//.....
//.....
}