Vince.Bdn
Vince.Bdn

Reputation: 1175

Scala 2.11.8 type tags not always properly generated by the compiler

Can someone explain to me (or redirect to resources) why in this particular case the type tag is not "properly" generated:

class A(s: Seq[_]*)

def toto[T: TypeTag](p: Seq[T]): Seq[T] = {
  println(typeTag[T].tpe)
  p
}

val data = Seq( ("a", "a") )
val x = data.map(_._1)
new A(
  toto(x),
  toto(data.map(_._2)),
  toto[String](data.map(_._2))
)

// output:
// java.lang.String
// Any
// String

As far as I understand, it seems that as my class A takes "untyped" (well with existential types) sequences, then the compiler does not bother generate the proper type tag when not required explicitly (though it does know the type of data.map(_._2) it still uses TypeTag[Any]... ). But it looks quite strange and I wondered if there was a more scientific explanation to this phenomenom.

Also, how can I force the compiler to generate a proper TypeTag[String] even if I don't want to create special variable (like this x variable above)?

Upvotes: 3

Views: 110

Answers (2)

Alexey Romanov
Alexey Romanov

Reputation: 170713

Nice problem! I have an explanation, but I am not certain it's right (80%, let's say).

As very often with Scala type inference question, you need to be aware of expected types. In this case, all arguments of new A are typed with expected type Seq[_], which is the same as Seq[Any] because of covariance. So:

  1. toto(data.map(_._2)) is typed with expected type Seq[Any]; data.map(_._2) is typed with expected type Seq[Any]. The signature of Seq#map is

    def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Seq[A], B, That]): That 
    

    so That is inferred based on the expected type and a suitable implicit bf is found. I am not actually sure if B is inferred to String or Any, but it probably doesn't matter.

  2. In the val x = data.map(_._1), there is no expected type so B is inferred to String, an implicit bf is found based on A and B and then That is inferred from the complete type of bf.

  3. toto(x) is typed with expected type Seq[Any]; x is typed with expected type Seq[Any], but it already has type Seq[String] and the expected type doesn't matter.

Upvotes: 2

Evgeny
Evgeny

Reputation: 1770

I would like to extend answer of @AlexeyRomanov by possible solution how to force compiler to evaluate specific type:

From here I took idea for forcing type difference:

sealed class =!=[A,B]
trait LowerPriorityImplicits {
  implicit def equal[A]: =!=[A, A] = sys.error("should not be called")
}
object =!= extends LowerPriorityImplicits {
  implicit def nequal[A,B](implicit same: A =:= B = null): =!=[A,B] =
    if (same != null) sys.error("should not be called explicitly with same type")
    else new =!=[A,B]
}

Now we can add limitation for parameter to toto:

class A(s: Seq[_]*)

def toto[T: TypeTag](p: Seq[T])(implicit guard: T =!= Any): Seq[T] = {
  println(typeTag[T].tpe)
  p
}

val data = Seq(("a", "a"))
val x = data.map(_._1)

new A(
  toto(x),
  toto(data.map(_._2)),
  toto[String](data.map(_._2))
)

And output I have

java.lang.String
java.lang.String
String

Upvotes: 1

Related Questions