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

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 ) 。TypeTagAny

于 2012-11-05T15:16:27.950 に答える
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 に答える
2

実際には、ここで msg の型をチェックしません。コンパイラは msg: T が消去されていることを警告します。そのため、TFilter で定義された型が関数 f で定義された型と同じであることを確認するだけです。

パターン マッチングはマニフェストと 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 に答える
0

みんなフィードバックありがとう。ClassTagパターン マッチングで を使用する必要がある理由がわかりました。

[SI-5143] Pattern matching on abstract types doesn't workを見つけることができました。関連付けられているコミットでClassTagは、パターンをチェック可能にするために のインスタンスが必要であると説明されています。

だから、はい、私はTypeTag間違って使用しました。パターン マッチングの場合は を使用する必要がありますClassTag

于 2012-11-05T19:43:26.240 に答える