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.
Related
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)
}
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).
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) => {
//.....
//.....
}
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.
From working on the first problem of the 99 Scala Puzzles I defined my own version of last like so:
def last[A](xs: List[A]): A = xs match {
case x :: Nil => x
case x :: xs => last(xs)
}
My question is: Why is it necessary for last to be directly followed by the type variable, as in last[A]? Why couldn't the compiler just do the right thing if I wrote the function like so:
def last(xs: List[A]): A
.....
(leaving the [A] off the end of last[A]?)
And if the compiler is capable of figuring it out, then what is the rationale for designing the language this way?
A appears 3 times:
last[A]
List[A]
: A(after the argument list)
The 2nd one is needed to specify that the List contains objects of type A. The 3rd one is needed to specify that the function returns an object of type A.
The 1st one is where you actually declare A, so it could be used in the other two places.
You need to write last[A] because A does not exist. Since it does not exist, by declaring it after the name of the function you actually get a chance to define some expectations or constraints for this type.
For example: last[A <: Int] to enforce the fact that A has to be a subtype of Int
Once it's declared, you can use it to define the type of your parameters and your return type.
I got an insight from #Lee's comment:
How would the compiler know that the A in List[A] doesn't refer to an
actual type called A?
To demonstrate to myself that this made sense, I tried substituting the type variable A, with the name of an actual type String, and then passed the function a List[Int], seeing that when last is declared like def last[String](xs: List[String]): String, I was able to pass last a List[Int]:
scala> def last[String](xs: List[String]): String = xs match {
| case x :: Nil => x
| case x :: xs => last(xs)
| }
last: [String](xs: List[String])String
scala> last(List(1,2,3,4))
res7: Int = 4
Therefore proving the identifier String does behave like a type variable, and does not reference the concrete type String.
It would also make debugging more difficult if the compiler just assumed that any identifier not in scope was a type variable. It therefore, makes sense to have to declare it at the beginning of the function definition.