Penggunaan TypeTag Scala 2.10

Saya sedang menggali api refleksi scala baru dan tidak tahu mengapa cuplikan berikut tidak berfungsi seperti yang diharapkan. Hirarki yang diberikan (mencoba menyederhanakan sebanyak yang saya bisa):

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)

Saya mengharapkan metode f untuk memfilter objek dengan tipe tertentu. Jadi cuplikan berikut akan mengembalikan Seq[Foo]

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

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

Dan itu sebenarnya mengembalikan Seq[Foo] tetapi dengan pesan lain tanpa filter, yang terdengar seperti bug.

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

Pertanyaan. Apakah saya menggunakan TypeTag salah atau cacat pada api refleksi baru?

PS0. Mencoba dengan Scala 2.10.0-RC1 dan 2.10.0-RC2

PS1. Solusinya adalah mengganti TypeTag dengan Manifest, sehingga dengan kode berikut collect secara berurutan akan mengembalikan List(Foo(1)) seperti yang diharapkan.

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

Pembaruan: Sama dengan rilis Scala 2.10.0-RC2 baru.


person 4e6    schedule 05.11.2012    source sumber
comment
Mungkin berguna untuk menambahkan peringatan yang Anda dapatkan untuk case msg: T di kode Anda: warning: abstract type T in type pattern T is unchecked since it is eliminated by erasure. Saya tidak melihat peringatan ini dengan pendekatan Manifest yang lama.   -  person Steve    schedule 05.11.2012
comment
@ som-snytt, menurut saya, ini sedang menggali lebih dalam. Kurangnya latihan bahasa Inggris. Saya akan mencoba untuk lebih berhati-hati lain kali :)   -  person 4e6    schedule 06.11.2012


Jawaban (4)


Jadi menurut saya masalah utama di sini adalah Anda harus mencocokkan dengan tipe msg, tetapi tipe waktu kompilasinya adalah Any (dari deklarasi PartialFunction). Pada dasarnya, Anda menginginkan TypeTag yang berbeda untuk setiap elemen di List[Any] Anda. Namun karena semuanya memiliki tipe waktu kompilasi Any karena semuanya dimasukkan ke dalam daftar yang sama, Anda tidak akan mendapatkan TypeTag yang lebih spesifik dari itu.

Saya rasa yang mungkin ingin Anda lakukan adalah menggunakan ClassTag alih-alih 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))

Seperti yang Ajran tunjukkan, sama seperti versi Manifest, Anda harus menyadari semua batasan jenis runtime termasuk masalah penghapusan dan tinju:

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)

Ada beberapa saran tentang cara membuat TypeTag lebih berguna untuk pencocokan pola (misalnya SI-6517), tapi menurut saya itu hanya akan membantu ketika Anda mencocokkan objek dengan TypeTag yang berguna, bukan objek dengan tipe waktu kompilasi Any.

person Steve    schedule 05.11.2012
comment
Namun karena scaladoc, ClassTag adalah kasus khusus yang lebih lemah dari TypeTag, jadi saya berasumsi bahwa kasus terakhir juga bisa digunakan. - person 4e6; 05.11.2012
comment
jika Anda melihat tautan dalam jawaban saya, Anda akan melihat bahwa bukan itu masalahnya: Kami pasti membahas penggunaan tag tipe untuk membantu pencocokan pola, tetapi, menurut saya, Adriaan memiliki hal-hal yang lebih penting untuk dilakukan saat ini. - person Arjan; 05.11.2012
comment
Benar, dokumennya mengatakan ClassTags adalah kasus khusus yang lebih lemah dari scala.reflect.api.TypeTags#TypeTags, karena hanya membungkus kelas runtime dari tipe tertentu, sedangkan TypeTag berisi semua informasi tipe statis. Namun jika tipe statis Anda adalah Any, TypeTag tidak akan membantu Anda, dan hal terbaik yang dapat Anda lakukan adalah kelas runtime dari ClassTag. - person Steve; 06.11.2012
comment
@Arjan, Steve, som-snytt, terima kasih atas bantuan Anda. Pikirkanlah, sekarang saya mengerti cara kerjanya. Semua jawaban memberikan informasi yang berguna, tetapi saya harus memilih satu. - person 4e6; 06.11.2012

sebenarnya Anda tidak memeriksa jenis pesan di sini, kompiler akan memperingatkan Anda bahwa pesan: T telah dihapus, jadi yang perlu Anda periksa hanyalah jenis yang ditentukan pada TFilter sama dengan jenis yang ditentukan pada fungsi f.

Sepertinya pencocokan pola "dibantu" oleh Manifest dan ClassTag, jadi msg: T memang tipe yang benar. Jika Anda mencobanya dengan primitif atau List[T] itu tidak akan berfungsi dengan benar.

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

Lihat diskusi ini: http://grokbase.com/t/gg/scala-user/126p8eh1w0/how-to-make-typetag-work-in-a-pattern

bug "Gunakan TypeTags saat mencocokkan pola jika tidak, tipe terhapus": di sini

person Arjan    schedule 05.11.2012

Hanya untuk bersenang-senang:

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])
}
person som-snytt    schedule 06.11.2012

Terima kasih semuanya atas masukannya. Pikirkan, saya telah menemukan alasan mengapa ClassTag harus digunakan dalam pencocokan pola.

Saya berhasil menemukan [SI-5143] Pencocokan pola pada tipe abstrak tidak berfungsi, dan komit terkait menjelaskan bahwa harus ada instance ClassTag yang harus dibuat pola dapat diperiksa.

Jadi, ya, saya salah menggunakan TypeTag; dalam hal pencocokan pola saya harus menggunakan ClassTag.

person 4e6    schedule 05.11.2012
comment
Lihat jawaban saya yang diperbarui - Saya pikir masalah sebenarnya adalah msg memiliki tipe waktu kompilasi Any. Ini bukan tipe abstrak, melainkan tipe konkrit. Itu hanya tipe beton yang tidak berguna. ;-) - person Steve; 06.11.2012