mardi 16 juin 2015

Scala ClassTag and Variance

What is the connection between variance and ClassTags or TypeTags?

I have two types T1 and T2, that are used as type params.

case class T1()
case class T2()

I have an abstract class with an invariant type parameter and one subclass, if I want to check the type of the type parameter it only works if there is no type check in the pattern, just in the guard. If a type test is present it always chooses the first case.

The type check is necessary because in my real code I want to call different functions for each type. There are separate functions for In[T1] and In[T2].

abstract class In[T]
case class VIn[T]() extends In[T]

def test1[T:ClassTag](v:In[T]) = list match {
  case x : VIn[T1@unchecked] if classTag[T] == classTag[T1] => "T1"
  case y : VIn[T2@unchecked] if classTag[T] == classTag[T2] => "T2"
}
test1(VIn[T1]())  //T1
test1(VIn[T2]())  //T1 !!!

def test2[T:ClassTag](v:In[T]) = list match {
  case x if classTag[T] == classTag[T1] => "T1"
  case y if classTag[T] == classTag[T2] => "T2"
}
test2(VIn[T1]())  //T1
test2(VIn[T2]())  //T2

While playing around with the List type that's use in many examples, I realized that if the type parameter is changed to be covariant it works in both tests.

abstract class Co[+T]
case class VCo[T]() extends Co[T]

def test1[T:ClassTag](v:Co[T]) = v match {
  case x : VCo[T1@unchecked] if classTag[T] == classTag[T1] => "T1"
  case y : VCo[T2@unchecked] if classTag[T] == classTag[T2] => "T2"
}
test1(VCo[T1]())  // T1
test1(VCo[T2]())  // T2

def test2[T:ClassTag](v:Co[T]) = v match {
  case x if classTag[T] == classTag[T1] => "T1"
  case y if classTag[T] == classTag[T2] => "T2"
}
test2(VCo[T1]())  // T1
test2(VCo[T2]())  // T2

Why does the first tests fail for invarinat types? There isn't a compiler warning, or a runtime error, it just picks the first case, but the guard is obviously false, as demonstrated in test2.





Aucun commentaire:

Enregistrer un commentaire