Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 989
Daniel C. Sobral Avatar asked Jul 07 '09 19:07

Daniel C. Sobral


People also ask

Does Scala have type erasure?

Type erasure refers to the runtime encoding of parameterized classes in Scala. It is simply performed by Scala compiler in which it removes all the generic type information after compilation. In Scala, generics are erased at runtime, which means that the runtime type of List[Int] and List[Boolean] is actually the same.

Why is type erasure needed?

Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.

What is TypeTag in Scala?

TypeTags#TypeTag . A full type descriptor of a Scala type. For example, a TypeTag[List[String]] contains all type information, in this case, of type scala.


1 Answers

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.

like image 131
Daniel C. Sobral Avatar answered Sep 24 '22 17:09

Daniel C. Sobral