繁体   English   中英

Scala宏和类型擦除

[英]Scala macro and type erasure

我在编写宏时遇到一些问题,该宏可以帮助我将表示为案例类实例的指标记录到InfluxDB。 我以为我遇到了类型擦除问题,并且tyep参数T丢失了,但是我不确定是怎么回事。 (这也是我第一次接触Scala宏。)

import scala.language.experimental.macros
import play.api.libs.json.{JsNumber, JsString, JsObject, JsArray}

abstract class Metric[T] {
    def series: String

    def jsFields: JsArray = macro MetricsMacros.jsFields[T]
    def jsValues: JsArray = macro MetricsMacros.jsValues[T]
}

object Metrics {
    case class LoggedMetric(timestamp: Long, series: String, fields: JsArray, values: JsArray)
    case object Kick

    def log[T](metric: Metric[T]): Unit = {
        println(LoggedMetric(
            System.currentTimeMillis,
            metric.series,
            metric.jsFields,
            metric.jsValues
        ))
    }
}

这是一个度量标准案例类的示例:

case class SessionCountMetric(a: Int, b: String) extends 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"]

这是实际的宏本身:

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

object MetricsMacros {
    private def fieldNames[T: c.WeakTypeTag](c: Context)= {
        val tpe = c.weakTypeOf[T]
        tpe.decls.collect {
            case field if field.isMethod && field.asMethod.isCaseAccessor => field.asTerm.name
        }
    }

    def jsFields[T: c.WeakTypeTag](c: Context) = {
        import c.universe._
        val names = fieldNames[T](c)
        Apply(
            q"play.api.libs.json.Json.arr",
            names.map(name => Literal(Constant(name.toString))).toList
        )
    }

    def jsValues[T: c.WeakTypeTag](c: Context) = {
        import c.universe._
        val names = fieldNames[T](c)
        Apply(
            q"play.api.libs.json.Json.arr",
            names.map(name => q"${c.prefix.tree}.$name").toList
        )
    }
}

更新资料

我像这样尝试了尤金的第二个建议:

abstract class Metric[T] {
    def series: String
}

trait MetricSerializer[T] {
    def fields: Seq[String]
    def values(metric: T): Seq[Any]
}

object MetricSerializer {
    implicit def materializeSerializer[T]: MetricSerializer[T] = macro MetricsMacros.materializeSerializer[T]
}

object Metrics {
    def log[T: MetricSerializer](metric: T): Unit = {
        val serializer = implicitly[MetricSerializer[T]]
        println(serializer.fields)
        println(serializer.values(metric))
    }
}

现在宏看起来像这样:

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 -特别是当它implicitly[MetricSerializer[T]]调用implicitly[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 values(metric: Metric[$tpe]) = $values

本来应该

def values(metric: $tpe) = $values

您所处的情况与最近的问题非常接近: scala宏:延迟类型推断

就目前而言,您必须将log转换为宏。 另一种选择是将Metric.jsFieldsMetric.jsValues转换成JsFieldableJsValuable类型类, JsValuable类由log调用站点上的隐式宏实现( http://docs.scala-lang.org/overviews/macros/implicits.html )。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM