Scala 2.10.2调用具有通用类型的“宏方法"不起作用 [英] scala 2.10.2 calling a 'macro method' with generic type not work

查看:83
本文介绍了Scala 2.10.2调用具有通用类型的“宏方法"不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我定义以下宏以将案例字段转换为映射

I define following macro to transform case fields to map

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

    def asMap_impl[T: c.WeakTypeTag](c: Context)(t: c.Expr[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(t.tree, m.name))
          reify(name.splice -> value.splice).tree
      }

      c.Expr[Map[String, Any]](Apply(mapApply, pairs.toList))
    }

方法实现

def asMap[T](t: T) = macro asMap_impl[T]

然后我定义一个案例类进行测试

Then I define a case class to test it

case class User(name : String)

工作正常(与scala repl一起使用):

It works fine(with scala repl):

 scala> asMap(User("foo")) res0:
 scala.collection.immutable.Map[String,String] = Map(name -> foo)

但是当我将此方法与其他通用方法包装在一起

def printlnMap[T](t: T) = println(asMap(t))

此方法始终打印空白地图:

This method always print empty map:

scala> printlnMap(User("foo"))
Map()

类型信息似乎丢失了,如何获取printlnMap来打印所有字段?

The type information seems lost, how to get the printlnMap to print all fields ?

推荐答案

之所以不起作用,是因为在编译printlnMap函数时,您的宏只会被调用一次.这样,它将把T视为抽象类型.每次调用printlnMap时都不会调用该宏.

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.

一种快速解决此问题的方法是将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]
}

然后,实现一个宏,该宏将为某些案例类T 实现该类型类的实例.您可以将其放入CaseClassToMap随播对象中,以便全局可见.

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

这篇关于Scala 2.10.2调用具有通用类型的“宏方法"不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆