scalascala-macrosscala-2.13

In scala macro, how to retrieve full type information from a WeakTypeTag?


I'm writing macro to convert a type description into a singleton type:

object Type2String {

  def apply[I]: Witness.Lt[String] = macro Macros.apply[I]

  final class Macros(val c: whitebox.Context) extends MWithReflection {

    import c.universe._

    def apply[A: WeakTypeTag]: Tree = {

      val tt: Type = weakTypeOf[A]
      val str = tt.toString

      // val vv = viz(tt)

      q"shapeless.Witness.mkWitness[$str]($str)"
    }
  }
}

The problem is that since A only has a WeakTypeTag. It cannot extract the correct info from generic types:


  case class ^^[T1, T2]() {

    final val wTSelf = Type2String[^^[T1, T2]]
  }


    val e1 = ^^[Int, String]()

    e1.wTSelf

This gives the wrong Witness type: shapeless.Witness.Aux[String("T1 ^^ T2")]

So my questions are:

  1. It is compile-time, the type information should be fully visible, why are T1 and T2 erased?

  2. How to fix this program so it gives the correct type info:

shapeless.Witness.Aux[String("Int ^^ String")]

?


Solution

  • For the first question, @oleg-pyzhcov pointed out very clearly the problem: your macro is expanded when you define the class, and here the compiler can't know what actual type is used.

    For the second question, @oleg-pyzhcov is right again. Indeed there is a similar example that uses implicit to solve your problem (scala 2.10.2 calling a 'macro method' with generic type not work).

    So I have tried to change your code a little in order to successufly return the right type:

    import scala.reflect.macros.whitebox
    import scala.language.experimental.macros
    trait Type2String[T] {
      def apply() : Witness.Lt[String]
    }
    
    object Type2String {
      implicit def materializeType2String[T]: Type2String[T] = macro impl[T]
    
      def impl[T: c.WeakTypeTag](c: whitebox.Context): c.Expr[Type2String[T]] = {
        import c.universe._
        val tt: Type = weakTypeOf[T]
        val str = tt.toString
        //val vv = viz(tt) I don't know what is viz(tt)...
    
        reify {
          () => c.Expr[Witness.Lt[String]](q"shapeless.Witness.mkWitness[$str]($str)").splice
        }
      }
    }
    

    and then you have to should write a little more code in the class definition:

    case class ^^[T1, T2]()(implicit val type2String: Type2String[^^[T1, T2]]) {
      final val wTSelf = type2String()
    }
    //or
    case class ^^^[T1, T2]() {
      final def wTSelf(implicit type2String: Type2String[^^[T1, T2]]) = type2String()
    }
    
    val e1 = ^^[Int, String]()
    //val e2 = ^^^[String, Double]()
    e1.wTSelf
    

    And that is.