Search code examples
scalapath-dependent-type

How to get the classtag of a path-dependent type


I have an abstract path-dependent type that I need to ClassTag of. Is there a better way than manually pulling the implicit for each concrete derived class?

trait Foo {
  type A // : ClassTag // Need the ClassTag of A later
  val ctA: ClassTag[A] // But can't put a context-bound on the type
}

class IntFoo extends Foo {
  type A = Int
  val ctA = implicitly[ClassTag[Int]]
}

class StringFoo extends Foo {
  type A = String
  val ctA = implicitly[ClassTag[String]]
}

Solution

  • You have to conjure a class tag where you know the type.

    scala> :pa
    // Entering paste mode (ctrl-D to finish)
    
    trait Foo {
    type A
    def ct[B: ClassTag] = implicitly[ClassTag[B]]
    }
    
    // Exiting paste mode, now interpreting.
    
    defined trait Foo
    
    scala> :pa
    // Entering paste mode (ctrl-D to finish)
    
    class IntFoo extends Foo {
    type A = Int
    def myct = ct[A]
    }
    
    // Exiting paste mode, now interpreting.
    
    defined class IntFoo
    
    scala> new IntFoo().myct
    res2: scala.reflect.ClassTag[Int] = Int
    

    But macros are pretty easy to write these days.

    scala> :pa
    // Entering paste mode (ctrl-D to finish)
    
    object M {
    def ct[A: c.WeakTypeTag](c: Context): c.Expr[ClassTag[A]] = {
    import c.universe._
    val a = c.prefix.tree.tpe.member(TypeName("A")).typeSignature
    c.Expr(q"implicitly[ClassTag[$a]]").asInstanceOf[c.Expr[ClassTag[A]]]
    }}
    
    // Exiting paste mode, now interpreting.
    
    scala> class Foo { type A = Int ; def f: ClassTag[A] = macro M.ct[A] }
    defined class Foo
    
    scala> new Foo().f
    res15: scala.reflect.ClassTag[Int] = Int
    
    scala> class Bar { type A = Char ; def f: ClassTag[A] = macro M.ct[A] }
    defined class Bar
    
    scala> new Bar().f
    res16: scala.reflect.ClassTag[Char] = Char
    

    so

    scala> trait Foo { type A ; def ct = macro M.ct[A] }
    defined trait Foo
    
    scala> class Bar extends Foo { type A = Int ; def p = println(ct) }
    defined class Bar
    
    scala> new Bar().p
    Int