scala 2.10.2 calling a 'macro method' with generic type not work

后端 未结 1 890
北海茫月
北海茫月 2021-02-08 02:58

I define following macro to transform case fields to map

   import scala.language.experimental.macros
    import scala.reflect.macros.Context

    def asMap_impl         


        
相关标签:
1条回答
  • 2021-02-08 03:24

    The reason why this doesn't work is that your macro will be called only once - when compiling printlnMap function. This way it will see T as an abstract type. The macro will not be called on each invocation of printlnMap.

    One way to quickly fix this is to implement printlnMap also as a macro. Of course, this is not ideal. So, here's a different approach - materialization of typeclass instances:

    First, define a typeclass that will allow us to convert case class instances to maps:

    trait CaseClassToMap[T] {
      def asMap(t: T): Map[String,Any]
    }
    

    Then, implement a macro that will materialize an instance of this type class for some case class T. You can put it into CaseClassToMap companion object so that it is visible globally.

    object CaseClassToMap {
      implicit def materializeCaseClassToMap[T]: CaseClassToMap[T] = macro impl[T]
    
      def impl[T: c.WeakTypeTag](c: Context): c.Expr[CaseClassToMap[T]] = {
        import c.universe._
    
        val mapApply = Select(reify(Map).tree, newTermName("apply"))
    
        val pairs = weakTypeOf[T].declarations.collect {
          case m: MethodSymbol if m.isCaseAccessor =>
            val name = c.literal(m.name.decoded)
            val value = c.Expr(Select(Ident(newTermName("t")), m.name))
            reify(name.splice -> value.splice).tree
          }
    
        val mapExpr = c.Expr[Map[String, Any]](Apply(mapApply, pairs.toList))
    
        reify {
          new CaseClassToMap[T] {
            def asMap(t: T) = mapExpr.splice
          }
        }
      }
    }
    

    Now, you can do this:

    def asMap[T: CaseClassToMap](t: T) =
      implicitly[CaseClassToMap[T]].asMap(t)
    
    def printlnMap[T: CaseClassToMap](t: T) =
      println(asMap(t))
    
    0 讨论(0)
提交回复
热议问题