Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why was Manifest deprecated? When should I use ClassTag and when should I use TypeTag

Tags:

scala

I have several questions on Manifest and TypeTag. I understand that JVM does not know about Generics and erases the types. So I cannot do this

def factoryForAll[T] = new T // will not compile. Runtime doesn't know what T is

Scala compiler could transfer information about types to runtime using Manifest (now deprecated). Manifest has methods like erasure which contains information about types. So I could do following to create objects of a generic type T

def factoryForall[T](implicit ev:Manifest[T]) = ev.erasure.newInstance

scala> factoryForAll[String]
res1:Any=""

scala> class C
defined class C

scala> factoryForAll[C]
res5: Any = C@52cb52bd

Question 1 - Interesting, it doesn't work for Int (or Float)? Why?

scala> factoryForAll[Int]
java.lang.InstantiationException: int

Question 2 - Why was Manifest deprecated? I understand that the newer version, TypeTag has richer information but I do not understand what were the shortcomings in Manifest

Question 3 - Scala 2.12 still has Manifest class (https://www.scala-lang.org/api/current/scala/reflect/Manifest.html). If Manifest are bad, why do Scala still has it? The documentation refers to using Manifest to create Arrays of Generic types but the Arrays could be implemented by ClassTag as well. So why does Scala still has Manifest?

scala> def makeArray[T](len:Int)(implicit ev:ClassTag[T]) = new Array[T](len)
makeArray: [T](len: Int)(implicit ev: scala.reflect.ClassTag[T])Array[T]

scala> makeArray[String](4)
res39: Array[String] = Array(null, null, null, null)

scala> makeArray[Int](4)
res40: Array[Int] = Array(0, 0, 0, 0)

scala> val al = makeArray[List[Int]](2)
al: Array[List[Int]] = Array(null, null)

scala> al(0) = List(1)

scala> al(1) = List(2,3)

Coming to TypeTag, there are 3 types. Referring to Scala documentation (http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html) and a tutorial on Medium (https://medium.com/@sinisalouc/overcoming-type-erasure-in-scala-8f2422070d20), I understand that TypeTag and ClassTag have different use cases. ClassTag cannot differentiate between types beyond 1st level of erasure.

//method to extract a type from a collection
def extractType[T](col:Iterable[Any])(implicit ev:ClassTag[T]) = {
val it =col.iterator
while (it.hasNext) {
val el = it.next
el match {
case x:T => println("got T")
case _ => println("not T")
}}}

extractType: [T](col: Iterable[Any])(implicit ev: scala.reflect.ClassTag[T])Unit

scala> extractType[Int](List(1,2,3,"hello"))
got T
got T
got T
not T

scala> extractType[List[Int]](List(List(1),List(2),List(3),List("hello")))
got T
got T
got T
got T //this should be not T

Question4: If ClassTag cannot differentiate between 1st level of erasure, why do I get the following error when I try to add a String in a List[Set[Int]]. Isn't Int erased?

scala> def makeArray[T](len:Int)(implicit ev:ClassTag[T]) = new Array[T](len)
makeArray: [T](len: Int)(implicit ev: scala.reflect.ClassTag[T])Array[T]

scala> val al = makeArray[List[Set[Int]]](2)
al: Array[List[Set[Int]]] = Array(null, null)

scala> al(0) = List(Set(2))

scala> al(1) = List(Set("2"))
<console>:28: error: type mismatch;
 found   : String("2")
 required: Int
       al(0) = List(Set("2"))
                        ^

Question5 - why in the earlier example extractType[List[Int]](List(List(1),List(2),List(3),List("hello"))), Scala couldn't distinguish String from Int but it distinguished between al(0) = List(Set(2)) and al(1) = List(Set("2"))

Question 6 - How do I change the extractType function such that I can check embedded types. I know I have to use TypeTag but I do not know how to check the type of the element in the collection.

def extractType[T](col:Iterable[Any])(implicit ev:TypeTag[T]) = {
    println("class is "+ev.mirror.runtimeClass) //I suppose in TypeTag, runtime is here
    val it =col.iterator
    while (it.hasNext) {
    val el = it.next
    el match {
        case x:T => println("got T") //this doesn't compile. What should I check for?
        case _ => println("not T")
    }}}
like image 315
Manu Chadha Avatar asked Oct 29 '22 22:10

Manu Chadha


1 Answers

Question 1: Int and Float aren't represented by classes in JVM (though they have corresponding Class objects), let alone ones which have parameterless constructors. Despite the forAll in the name, this works for a very limited set of types.

Question 2: Manifest mixes up concerns which ClassTag and TypeTag separate.

Question 3: if you look at the source of Manifest, it says

// TODO undeprecated until Scala reflection becomes non-experimental 
// @deprecated("use scala.reflect.ClassTag (to capture erasures) or scala.reflect.runtime.universe.TypeTag (to capture types) or both instead", "2.10.0") 

Question 4/5: this error comes from the static type al: Array[List[Set[Int]]]. No runtime information provided by ClassTag or TypeTag is involved.

Question 6: using only the standard library you can't. But see Shapeless.

like image 101
Alexey Romanov Avatar answered Nov 15 '22 07:11

Alexey Romanov