Scala 2.11.8类型标记并不总是由编译器正确生成

时间:2018-03-15 15:14:54

标签: scala

有人可以向我解释(或重定向到资源)为什么在这种特殊情况下,类型标签不是"正确"产生的:

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

据我了解,似乎我的班级A需要"无类型" (以及存在类型)序列,然后编译器在没有明确要求时不会生成正确的类型标记(尽管它确实知道data.map(_._2)的类型仍然使用TypeTag[Any] ...)。但它看起来很奇怪,我想知道这个现象是否有更科学的解释。

另外,即使我不想创建特殊变量(如上面的TypeTag[String]变量),如何强制编译器生成正确的x

2 个答案:

答案 0 :(得分:2)

好的问题!我有一个解释,但我不确定它是对的(80%,让我们说)。

与Scala类型推断问题一样,您需要了解预期的类型。在这种情况下,new A的所有参数都使用预期类型Seq[_]输入,由于协方差,它与Seq[Any]相同。所以:

  1. toto(data.map(_._2))输入预期类型Seq[Any];输入data.map(_._2)预期类型Seq[Any]Seq#map的签名是

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

    因此根据预期类型推断出That,并找到合适的隐式bf。我不确定B是推断到String还是Any,但它可能并不重要。

  2. val x = data.map(_._1)中,没有预期的类型,因此B被推断为String,根据bf找到隐式AB的完整类型推断That然后bf

  3. toto(x)输入预期类型Seq[Any];使用预期类型x键入Seq[Any],但它已经有类型Seq[String],并且预期类型无关紧要。

答案 1 :(得分:1)

我想通过可能的解决方案扩展@AlexeyRomanov的答案,如何强制编译器评估特定类型:

here我想出了强制类型差异的想法:

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]
}

现在我们可以将参数限制添加到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))
)

输出我

java.lang.String
java.lang.String
String