Scala 2.10 TypeTag用法

时间:2012-11-05 14:04:24

标签: scala reflection scala-2.10

我正在挖掘新的scala反射api,并且无法弄清楚为什么以下代码段不能按预期工作。给定层次结构(试图尽可能地简化):

import scala.reflect.runtime.universe._

trait TF[A] {
  implicit def t: TypeTag[A]

  def f[T <: A: TypeTag]: PartialFunction[Any, A] = {
    case msg: T if typeOf[T] =:= typeOf[A] => msg
  }
}

class TFilter[T: TypeTag] extends TF[T] {
  def t = typeTag[T]
}

case class Foo(x: Int)

我希望方法f能够过滤给定类型的对象。因此,以下代码段应返回Seq[Foo]

val messages = Seq(1, "hello", Foo(1))

val tFilter = new TFilter[Foo]
messages collect tFilter.f[Foo]

它实际上会返回Seq[Foo],但其他消息未经过滤,这听起来像是一个错误。

res1: Seq[Foo] = List(1, hello, Foo(1))

问题即可。我使用TypeTag错了还是新反映api的缺陷?

PS0 即可。尝试使用Scala 2.10.0-RC12.10.0-RC2

PS1 即可。解决方法是将TypeTag替换为Manifest,因此序列中的以下代码collect将按预期返回List(Foo(1))

trait MF[A] {
  implicit def m: Manifest[A]

  def f[T <: A: Manifest]: PartialFunction[Any, A] = {
    case msg: T if typeOf[T] =:= typeOf[A] => msg
  }
}

class MFilter[T: Manifest] extends MF[T] {
  def m = manifest[T]
}

更新:与新Scala 2.10.0-RC2版本相同。

4 个答案:

答案 0 :(得分:6)

所以我认为这里的关键问题是你需要匹配msg的类型,但它的编译时类型是Any(来自PartialFunction声明)。从本质上讲,您需要为TypeTag中的每个元素添加不同的List[Any]。但是因为它们都被编入时间类型为Any,因为所有这些都被放入同一个列表中,所以你不会得到一个比这更具体的TypeTag

我认为您可能想要做的是使用ClassTag代替TypeTag

trait TF[A] {
  implicit def t: ClassTag[A]

  def f: PartialFunction[Any, A] = {
    case msg: A => msg
  }
}

class TFilter[T: ClassTag] extends TF[T] {
  def t = classTag[T]
}

case class Foo(x: Int)

val messages = Seq(1, "hello", Foo(1), List(1), List("a"))
messages collect new TFilter[Foo].f // produces List(Foo(1))

作为Ajran points out,就像Manifest版本一样,您必须了解运行时类型的所有限制,包括擦除和装箱问题:

messages collect new TFilter[List[Int]].f // produces List(List(1), List("a"))
messages collect new TFilter[Int].f // produces List()
messages collect new TFilter[java.lang.Integer].f // produces List(1)

有一些关于如何使TypeTag对模式匹配更有用的建议(例如SI-6517),但我认为这些只会在你使用有用的{{ 1}},而不是编译时类型为TypeTag的对象。

答案 1 :(得分:2)

实际上你没有在这里检查msg的类型,编译器会警告你msg:T被删除了,所以你要检查的是TFilter上定义的类型与函数f上定义的类型相同。 / p>

我看起来模式匹配是由Manifest和ClassTag“辅助”的,所以msg:T确实是一个正确的类型。如果您使用基元或List [T]进行尝试,它将无法正常工作。

val mFilter = new MFilter[Int]
messages collect mFilter.f[Int] // res31: Seq[Int] = List()

val messages = List(List(1), List("a"))
val mFilter = new MFilter[List[Int]]
messages collect mFilter.f[List[Int]] // res32: List[List[Int]] = List(List(1), List(a))

看看这个讨论: http://grokbase.com/t/gg/scala-user/126p8eh1w0/how-to-make-typetag-work-in-a-pattern

bug“在模式匹配时使用TypeTags,否则将删除类型”:here

答案 2 :(得分:2)

只是为了好玩:

import scala.reflect._
import scala.reflect.runtime.{currentMirror=>cm,universe=>ru}
import ru._

object Test extends App {
  type MyTag[A] = TypeTag[A]
  //type MyTag[A] = ClassTag[A]

  trait TF[A] {
    implicit def t: MyTag[A]

    def f[T <: A: MyTag]: PartialFunction[Any, A] = {
      //case msg: T => msg            // ok for ClassTag
      case msg: T @unchecked if matching[T](msg) => msg
      //case msg: T if typeOf[T] =:= typeOf[A] => msg
    }
    def matching[T](a: Any)(implicit tt: TypeTag[T]) =
      (cm reflect a).symbol.toType weak_<:< tt.tpe
  }

  case class TFilter[A: MyTag]() extends TF[A] {
    def t = implicitly[MyTag[A]]
  }

  trait Foo { def x: Int }
  case class Bar(x: Int) extends Foo
  case class Baz(x: Int) extends Foo

  val messages = Seq(1, Bar(0), "hello", Baz(1))
  println(messages collect TFilter[Foo].f[Foo])
  println(messages collect TFilter[Foo].f[Bar])
}

答案 3 :(得分:0)

感谢大家的反馈。想想,我已经找到了ClassTag应该用于模式匹配的原因。

我设法找到了[SI-5143] Pattern matching on abstract types doesn't work,并且associated commit解释说应该有一个ClassTag的实例来使模式可以检查。

所以,是的,我使用TypeTag错了;在模式匹配的情况下,我应该使用ClassTag