简体   繁体   English

Scala 2.10 TypeTag用法

[英]Scala 2.10 TypeTag usage

I'm digging new scala reflection api and can't figure out why the following snippet doesn't work as expected. 我正在挖掘新的scala反射api,并且无法弄清楚为什么以下代码段不能按预期工作。 Given hierarchy (tried to simplify as much as I can): 给定层次结构(试图尽可能地简化):

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)

I expect method f to filter objects of given type. 我希望方法f过滤给定类型的对象。 So the following snippet should return Seq[Foo] 所以下面的片段应该返回Seq[Foo]

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

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

And it actually returns Seq[Foo] but with other messages unfiltered, which sounds like a bug. 它实际上返回Seq[Foo]但其他消息未经过滤,这听起来像一个bug。

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

Question . 问题 Am I using TypeTag wrong or it is defect of new reflection api? 我使用TypeTag错误还是新反射api的缺陷?

PS0 . PS0 Tried with Scala 2.10.0-RC1 and 2.10.0-RC2 尝试使用Scala 2.10.0-RC12.10.0-RC2

PS1 . PS1 The workaround is to replace TypeTag with Manifest , so with the following code collect on sequence will return List(Foo(1)) as expected. 解决方法是使用Manifest替换TypeTag ,因此使用以下代码序列中的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]
}

Update : Same with new Scala 2.10.0-RC2 release. 更新 :与新的Scala 2.10.0-RC2版本相同。

So I think the key problem here is that you need to match against the type of msg , but its compile-time type is Any (from the PartialFunction declaration). 所以我认为这里的关键问题是你需要匹配msg的类型,但它的编译时类型是Any (来自PartialFunction声明)。 Essentially, you want a different TypeTag for each element in your List[Any] . 从本质上讲,您需要为List[Any]每个元素使用不同的TypeTag But since they all have compile-time type of Any by virtue of all being put into the same list, you're not going to get a TypeTag that's any more specific than that. 但由于它们都被置于同一个列表中,因此它们都具有编译时类型的Any ,所以你不会得到比这更具体的TypeTag

I think what you probably want to do is use ClassTag instead of 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))

As Ajran points out , just like the Manifest version, you'll have to be aware of all the limitations of runtime types including erasure and boxing issues: 正如Ajran指出的那样 ,就像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)

There are some suggestions about how to make TypeTag more useful for pattern matching (eg SI-6517 ), but I think those will only help when you're matching against an object with an useful TypeTag , not an object with compile-time type of Any . 关于如何使TypeTag对模式匹配更有用(例如SI-6517 )有一些建议,但我认为这些只会在你用一个有用的TypeTag匹配一个对象时有用,而不是一个编译时类型为Any

actually you don't check the type of msg here, compiler will warn you that msg: T is erased, so all you are left checking is that type defined on TFilter is the same as a type defined on function f. 实际上你不在这里检查msg的类型,编译器会警告你msg:T被删除了,所以你要检查的是TFilter上定义的类型与函数f上定义的类型相同。

I looks like pattern matching is "assisted" by Manifest and ClassTag, so msg: T is indeed a correct type. 我看起来模式匹配是由Manifest和ClassTag“辅助”的,所以msg:T确实是一个正确的类型。 If you try it with primitives or List[T] it will not work correctly. 如果您使用基元或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))

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

bug "Use TypeTags when pattern-matching otherwise erased types": here bug“模式匹配时使用TypeTags,否则擦除类型”: 这里

Just for fun: 纯娱乐:

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

Thanks everyone to feedback. 谢谢大家的反馈。 Think, I've found the reason why ClassTag should be used in pattern matching. 想想,我发现了ClassTag应该用于模式匹配的原因。

I've managed to find [SI-5143] Pattern matching on abstract types doesn't work , and it's associated commit explains that there should be an instance of ClassTag to make pattern checkable. 我设法找到[SI-5143]抽象类型上的模式匹配不起作用 ,并且它的相关提交解释了应该有一个ClassTag实例来使模式可检查。

So, yes, I used TypeTag wrong; 所以,是的,我使用了TypeTag错了; in case of pattern matching I should use ClassTag . 在模式匹配的情况下,我应该使用ClassTag

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM