Scala 2.10 TypeTag usage

前端 未结 4 1840
温柔的废话
温柔的废话 2021-02-04 13:27

I\'m digging new scala reflection api and can\'t figure out why the following snippet doesn\'t work as expected. Given hierarchy (tried to simplify as much as I can):

         


        
相关标签:
4条回答
  • 2021-02-04 13:59

    So I think the key problem here is that you need to match against the type of msg, but its compile-time type is Any (from the PartialFunction declaration). Essentially, you want a different TypeTag for each element in your List[Any]. But since they all have compile-time type of Any by virtue of all being put into the same list, you're not going to get a TypeTag that's any more specific than that.

    I think what you probably want to do is use ClassTag instead of 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))
    

    As Ajran points out, just like the Manifest version, you'll have to be aware of all the limitations of runtime types including erasure and boxing issues:

    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)
    

    There are some suggestions about how to make TypeTag more useful for pattern matching (e.g. SI-6517), but I think those will only help when you're matching against an object with an useful TypeTag, not an object with compile-time type of Any.

    0 讨论(0)
  • 2021-02-04 14:07

    actually you don't check the type of msg here, compiler will warn you that msg: T is erased, so all you are left checking is that type defined on TFilter is the same as a type defined on function f.

    I looks like pattern matching is "assisted" by Manifest and ClassTag, so msg: T is indeed a correct type. If you try it with primitives or List[T] it will not work correctly.

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

    Look at this discussion: http://grokbase.com/t/gg/scala-user/126p8eh1w0/how-to-make-typetag-work-in-a-pattern

    bug "Use TypeTags when pattern-matching otherwise erased types": here

    0 讨论(0)
  • 2021-02-04 14:10

    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])
    }
    
    0 讨论(0)
  • 2021-02-04 14:16

    Thanks everyone to feedback. Think, I've found the reason why ClassTag should be used in pattern matching.

    I've managed to find [SI-5143] Pattern matching on abstract types doesn't work, and it's associated commit explains that there should be an instance of ClassTag to make pattern checkable.

    So, yes, I used TypeTag wrong; in case of pattern matching I should use ClassTag.

    0 讨论(0)
提交回复
热议问题