การใช้ TypeTag ของ Scala 2.10

ฉันกำลังขุด 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 ผิดหรือเป็นข้อบกพร่องของ Reflection API ใหม่

PS0 พยายามด้วย Scala 2.10.0-RC1 และ 2.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 ใหม่


person 4e6    schedule 05.11.2012    source แหล่งที่มา
comment
การเพิ่มคำเตือนที่คุณได้รับสำหรับ case msg: T ในโค้ดของคุณ: warning: abstract type T in type pattern T is unchecked since it is eliminated by erasure อาจเป็นประโยชน์ ฉันไม่เห็นคำเตือนนี้ด้วยวิธี Manifest แบบเก่า   -  person Steve    schedule 05.11.2012
comment
@som-snytt ลองคิดดูว่ามันกำลังเจาะลึกอยู่ ขาดการฝึกฝนภาษาอังกฤษ คราวหน้าจะพยายามระวังให้มากขึ้นครับ :)   -  person 4e6    schedule 06.11.2012


คำตอบ (4)


ดังนั้นฉันคิดว่าปัญหาสำคัญที่นี่คือคุณต้องจับคู่กับประเภทของ 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

person Steve    schedule 05.11.2012
comment
แต่เนื่องจาก scaladoc, ClassTag เป็นกรณีพิเศษที่อ่อนแอกว่าของ TypeTag ดังนั้นฉันคิดว่าอย่างหลังก็ควรจะได้ผลเช่นกัน - person 4e6; 05.11.2012
comment
หากคุณดูลิงก์ในคำตอบของฉัน คุณจะเห็นว่าไม่เป็นเช่นนั้น: เราได้พูดคุยกันอย่างแน่นอนว่าการใช้แท็กประเภทเพื่อช่วยในการจับคู่รูปแบบ แต่ฉันคิดว่า Adriaan มีสิ่งที่สำคัญกว่าที่ต้องทำในตอนนี้ - person Arjan; 05.11.2012
comment
ใช่แล้ว เอกสารบอกว่า ClassTags เป็นกรณีพิเศษที่อ่อนแอกว่าของ scala.reflect.api.TypeTags#TypeTags โดยจะรวมเฉพาะคลาสรันไทม์ของประเภทที่กำหนด ในขณะที่ TypeTag จะมีข้อมูลประเภทคงที่ทั้งหมด แต่หากประเภทคงที่ของคุณคือ Any TypeTag จะไม่ช่วยคุณอยู่ดี และสิ่งที่ดีที่สุดที่คุณสามารถทำได้คือคลาสรันไทม์จาก ClassTag - person Steve; 06.11.2012
comment
@Arjan, Steve, som-snytt ขอบคุณสำหรับความช่วยเหลือของคุณ คิดสิ ตอนนี้ฉันเข้าใจแล้วว่ามันทำงานอย่างไร คำตอบทั้งหมดให้ข้อมูลที่เป็นประโยชน์ แต่ฉันควรเลือกคำตอบใดข้อหนึ่ง - person 4e6; 06.11.2012

จริงๆ แล้วคุณไม่ได้ตรวจสอบประเภทของ 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 เมื่อจับคู่รูปแบบ มิฉะนั้นจะถูกลบประเภท": ที่นี่

person Arjan    schedule 05.11.2012

แค่เล่น ๆ:

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

ขอบคุณทุกคนที่ให้ข้อเสนอแนะ คิดว่าฉันพบเหตุผลว่าทำไมจึงควรใช้ ClassTag ในการจับคู่รูปแบบ

ฉันพบ [SI-5143] การจับคู่รูปแบบกับประเภทนามธรรมไม่ทำงาน และ การคอมมิตที่เกี่ยวข้อง อธิบายว่าควรมีอินสแตนซ์ของ ClassTag ที่จะทำ สามารถตรวจสอบรูปแบบได้

ใช่แล้ว ฉันใช้ TypeTag ผิด ในกรณีที่ตรงกับรูปแบบ ฉันควรใช้ ClassTag

person 4e6    schedule 05.11.2012
comment
ดูคำตอบที่อัปเดตของฉัน - ฉันคิดว่าปัญหาที่แท้จริงคือ msg มีประเภทเวลาคอมไพล์เป็น Any มันไม่ใช่ประเภทนามธรรม แต่เป็นประเภทที่เป็นรูปธรรม มันเป็นเพียงประเภทคอนกรีตที่ไร้ประโยชน์ ;-) - person Steve; 06.11.2012