Scala宏和类型擦除

Scala宏和类型擦除,scala,macros,type-erasure,scala-macros,Scala,Macros,Type Erasure,Scala Macros,我写的一个宏有一些问题,该宏帮助我将表示为案例类实例的度量记录到XDB。我假设我有一个类型擦除问题,并且tyep参数T丢失了,但是我不完全确定到底发生了什么。(这也是我第一次接触Scala宏。) 导入scala.language.experimental.macros 导入play.api.libs.json.{JsNumber,JsString,JsObject,JsArray} 抽象类度量[T]{ def系列:字符串 def jsFields:JsArray=macro MetricsMac

我写的一个宏有一些问题,该宏帮助我将表示为案例类实例的度量记录到XDB。我假设我有一个类型擦除问题,并且tyep参数T丢失了,但是我不完全确定到底发生了什么。(这也是我第一次接触Scala宏。)

导入scala.language.experimental.macros
导入play.api.libs.json.{JsNumber,JsString,JsObject,JsArray}
抽象类度量[T]{
def系列:字符串
def jsFields:JsArray=macro MetricsMacros.jsFields[T]
def jsValues:JsArray=macro MetricsMacros.jsValues[T]
}
对象度量{
案例类LoggedMetric(时间戳:长,系列:字符串,字段:JsArray,值:JsArray)
案例对象踢
定义日志[T](度量:度量[T]):单位={
println(对数计量(
System.currentTimeMillis,
公制系列,
metric.jsFields,
metric.jsValues
))
}
}
下面是一个示例度量案例类:

case类SessionCountMetric(a:Int,b:String)扩展了Metric[SessionCountMetric]{
val series=“sessioncount”
}
下面是我尝试记录时发生的情况:

scala> val m = SessionCountMetric(1, "a")
m: com.confabulous.deva.SessionCountMetric = SessionCountMetric(1,a)

scala> Metrics.log(m)
LoggedMetric(1411450638296,sessioncount,[],[])
尽管宏本身似乎工作正常:

scala> m.jsFields
res1: play.api.libs.json.JsArray = ["a","b"]

scala> m.jsValues
res2: play.api.libs.json.JsArray = [1,"a"]
下面是实际的宏本身:

导入scala.language.experimental.macros
导入scala.reflect.macros.blackbox.Context
对象度量宏{
私有def字段名[T:c.WeakTypeTag](c:Context)={
val tpe=c.weakTypeOf[T]
tpe.decls.collect{
如果field.isMethod&&field.asMethod.isCaseAccessor=>field.asTerm.name,则为case字段
}
}
def jsFields[T:c.WeakTypeTag](c:Context)={
导入c.universe_
val名称=字段名称[T](c)
申请(
q“play.api.libs.json.json.arr”,
names.map(name=>Literal(常量(name.toString))).toList
)
}
def jsValues[T:c.WeakTypeTag](c:Context)={
导入c.universe_
val名称=字段名称[T](c)
申请(
q“play.api.libs.json.json.arr”,
names.map(name=>q“${c.prefix.tree}.$name”).toList
)
}
}
更新 我试过尤金的第二个建议:

object MetricsMacros {
    def materializeSerializer[T: c.WeakTypeTag](c: Context) = {
        import c.universe._

        val tpe = c.weakTypeOf[T]
        val names = tpe.decls.collect {
            case field if field.isMethod && field.asMethod.isCaseAccessor => field.asTerm.name
        }

        val fields = Apply(
            q"Seq",
            names.map(name => Literal(Constant(name.toString))).toList
        )

        val values = Apply(
            q"Seq",
            names.map(name => q"metric.$name").toList
        )

        q"""
            new MetricSerializer[$tpe] {
                def fields = $fields
                def values(metric: Metric[$tpe]) = $values
            }
        """
    }
}
抽象类度量[T]{
def系列:字符串
}
特征度量序列器[T]{
定义字段:Seq[字符串]
def值(公制:T):序号[任何]
}
对象度量序列化器{
隐式定义materializeSerializer[T]:MetricSerializer[T]=宏MetricsMacros.materializeSerializer[T]
}
对象度量{
def日志[T:MetricSerializer](公制:T):单位={
val serializer=隐式[MetricSerializer[T]]
println(序列化程序字段)
println(序列化程序值(公制))
}
}
宏现在看起来如下所示:

object MetricsMacros {
    def materializeSerializer[T: c.WeakTypeTag](c: Context) = {
        import c.universe._

        val tpe = c.weakTypeOf[T]
        val names = tpe.decls.collect {
            case field if field.isMethod && field.asMethod.isCaseAccessor => field.asTerm.name
        }

        val fields = Apply(
            q"Seq",
            names.map(name => Literal(Constant(name.toString))).toList
        )

        val values = Apply(
            q"Seq",
            names.map(name => q"metric.$name").toList
        )

        q"""
            new MetricSerializer[$tpe] {
                def fields = $fields
                def values(metric: Metric[$tpe]) = $values
            }
        """
    }
}
但是,当我调用
Metrics.log
——特别是当它隐式调用
[MetricSerializer[T]]
时,我得到以下错误:

error: value a is not a member of com.confabulous.deva.Metric[com.confabulous.deva.SessionCountMetric]
为什么它试图使用
Metric[com.confabulous.deva.SessionCountMetric]
而不是
SessionCountMetric

结论 修好了

def值(公制:公制[$tpe])=$value
应该是

def值(度量:$tpe)=$values

您所处的情况与最近一个问题中描述的情况非常接近:


就目前情况而言,您必须将
log
转换为宏。另一种方法是将
Metric.jsFields
Metric.jsValues
转换为
JsFieldable
jsvaluel
类型类,这些类由
log
()的调用站点上的隐式宏具体化。

我尝试了您的替代方法,因为总体上看起来更干净,但后来遇到了另一个问题。请看我的编辑中的更新。事实上,这是一个快速修复-见结论。谢谢