2012-11-05 13 views
10

Ich graben neue Scala Reflexion API und kann nicht herausfinden, warum das folgende Snippet nicht wie erwartet funktioniert. Angesichts Hierarchie (versucht, so viel zu vereinfachen, wie ich kann):Scala 2.10 TypeTag Verwendung

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) 

Ich erwarte Methode f auf Objekte von bestimmtem Typ zu filtern. So sollte die folgenden Ausschnitt zurückkehren Seq[Foo]

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

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

Und es gibt tatsächlich Seq[Foo] aber mit anderen Meldungen ungefiltert, was wie ein Fehler klingt.

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

Frage. Benutze ich TypeTag falsch oder es ist Defekt der neuen Reflexion API?

PS0. Versucht mit Scala 2.10.0-RC1 und 2.10.0-RC2

PS1. Die Problemumgehung besteht darin, TypeTag durch Manifest zu ersetzen, sodass mit dem folgenden Code collect on sequence wie erwartet List(Foo(1)) zurückgegeben wird.

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

aktualisieren: Samt mit neuer Scala 2.10.0-RC2 Release.

+0

Es könnte hilfreich sein, um die Warnung Sie 'Fall msg erhalten hinzuzufügen: T' im Code:' Warnung: abstrakten Typ T in Art Muster T nicht markiert ist, da es durch Löschen beseitigt ". Ich sehe diese Warnung nicht mit dem alten 'Manifest'-Ansatz. – Steve

+0

@ Som-Snytt, denke, es ist * graben *. Mangel an Englisch üben. Ich werde versuchen, beim nächsten Mal vorsichtiger zu sein :) – 4e6

Antwort

6

Also ich denke, das Hauptproblem ist, dass Sie gegen übereinstimmen müssen der Typ msg, aber sein Kompilierzeittyp ist Any (aus der PartialFunction Deklaration). Im Wesentlichen möchten Sie ein anderes TypeTag für jedes Element in Ihrem List[Any]. Aber da sie alle den Kompilierungszeittyp Any haben, indem sie alle in die gleiche Liste gesetzt werden, erhalten Sie keine TypeTag, die spezifischer ist.

Ich denke, was Sie wahrscheinlich tun wollen, ist ClassTag statt TypeTag zu verwenden:

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

Wie Ajran points out, genau wie die Manifest Version, werden Sie von allen bewusst sein, die Grenzen der Laufzeittypen, einschließlich Löschen und Box-Probleme:

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) 

Es gibt einige Vorschläge, wie TypeTag nützliche für den Mustervergleich zu machen (zB SI-6517), aber ich denke, diejenigen, wird nur helfen, wenn Sie gegen ein Objekt mit einem nützlichen TypeTag, kein Objekt mit Kompilierungszeit Typ von Any übereinstimmen.

+0

Aber wegen [scaladoc] (http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/index.html# scala.reflect.ClassTag), 'ClassTag' ist ein schwächerer Spezialfall von' TypeTag', also gehe ich davon aus, dass letzteres auch funktionieren sollte. – 4e6

+0

Wenn Sie einen Link in meiner Antwort betrachten, werden Sie sehen, dass das nicht der Fall ist: "Wir haben auf jeden Fall die Verwendung von Typ-Tags diskutiert, um Pattern Matching zu unterstützen, aber ich denke, Adriaan hat jetzt wichtigere Dinge zu tun." – Arjan

+0

Richtig, die Dokumente sagen ["ClassTags sind ein schwächerer Sonderfall von scala.reflect.api.TypeTags # TypeTags, da sie nur die Laufzeitklasse eines bestimmten Typs umschließen, während ein TypeTag alle statischen Typinformationen enthält"] (http : //www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/index.html#scala.reflect.ClassTag). Aber wenn Ihr statischer Typ 'Any 'ist, wird Ihnen' TypeTag' sowieso nicht helfen, und das Beste, was Sie tun können, ist die Laufzeitklasse aus dem 'ClassTag'. – Steve

2

tatsächlich überprüfen Sie nicht den Typ von msg hier, Compiler wird Sie warnen, dass msg: T ist gelöscht, so alles, was Sie links überprüfen, ist dieser Typ auf TFilter definiert ist der gleiche wie ein Typ für Funktion f definiert.

Ich sehe aus wie Mustererkennung ist "unterstützt" von Manifest und ClassTag, so msg: T ist in der Tat ein richtiger Typ. Wenn Sie es mit Primitiven oder List [T] versuchen, wird es nicht richtig funktionieren.

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

Schauen Sie sich diese Diskussion: http://grokbase.com/t/gg/scala-user/126p8eh1w0/how-to-make-typetag-work-in-a-pattern

Bug „Use TypeTags wenn Mustervergleich sonst gelöscht Typen“: here

0

Vielen Dank an alle Feedback. Denken Sie, ich habe den Grund gefunden, warum ClassTag im Mustervergleich verwendet werden sollte.

Ich habe es geschafft, [SI-5143] Pattern matching on abstract types doesn't work zu finden, und es ist associated commit erklärt, dass es eine Instanz von ClassTag sein sollte, um Muster prüfbar zu machen.

Also ja, ich habe TypeTag falsch verwendet; im Falle eines Mustervergleichs sollte ich ClassTag verwenden.

+0

Siehe meine aktualisierte Antwort - Ich denke, das eigentliche Problem ist, dass 'msg' einen Kompilierungs-Typ von' Any' hat. Es ist kein abstrakter Typ, es ist ein konkreter Typ. Es ist nur ein nutzloser Betontyp. ;-) – Steve

2

Just for fun:

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