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的以下代码将按预期返回。collectList(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

4 回答 4

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 指出的那样,就像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),但我认为这些建议只有在您匹配一个有用的对象时才有帮助TypeTag,而不是编译时类型为Any.

于 2012-11-05T15:16:27.950 回答
2

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

我看起来模式匹配是由 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

错误“在模式匹配其他被删除的类型时使用 TypeTags”:这里

于 2012-11-05T15:11:36.997 回答
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])
}
于 2012-11-06T03:39:23.153 回答
0

感谢大家的反馈。想一想,我找到了为什么ClassTag应该在模式匹配中使用的原因。

我设法找到[SI-5143] 抽象类型上的模式匹配不起作用,并且相关的提交解释说应该有一个实例ClassTag来使模式可检查。

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

于 2012-11-05T19:43:26.240 回答