【问题标题】:Scala 2.10 TypeTag usageScala 2.10 TypeTag 用法
【发布时间】:2012-10-25 09:13:41
【问题描述】:

我正在挖掘新的 scala 反射 api,但无法弄清楚为什么以下 sn-p 不能按预期工作。给定层次结构(尽量简化):

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 过滤给定类型的对象。所以下面的 sn-p 应该返回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 版本相同。

【问题讨论】:

  • 在代码中添加case msg: T 的警告可能会有所帮助:warning: abstract type T in type pattern T is unchecked since it is eliminated by erasure。使用旧的 Manifest 方法时,我看不到此警告。
  • @som-snytt,想想,它正在深入研究。缺乏英语练习。下次我会更加小心:)

标签: scala reflection scala-2.10


【解决方案1】:

所以我认为这里的关键问题是你需要匹配msg 的类型,但它的编译时类型是Any(来自PartialFunction 声明)。本质上,您希望List[Any] 中的每个元素都有一个不同的TypeTag。但是由于它们都具有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),但我认为这些建议只会在您匹配具有有用TypeTag 的对象时有所帮助,而不是编译时类型为 Any 的对象。

【讨论】:

【解决方案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 否则已擦除的类型”:here

【讨论】:

    【解决方案3】:

    只是为了好玩:

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

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

      • 查看我更新的答案 - 我认为真正的问题是 msg 的编译时类型为 Any。它不是抽象类型,而是具体类型。它只是一种无用的混凝土类型。 ;-)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-20
      • 1970-01-01
      相关资源
      最近更新 更多