【问题标题】:Scala macro and type erasureScala 宏和类型擦除
【发布时间】:2014-09-23 05:44:53
【问题描述】:

我在编写宏时遇到了一些问题,该宏用于帮助我将表示为案例类实例的指标记录到 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
        )
    }
}

更新

我尝试了 Eugene 的第二个建议是这样的:

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]] 时,我收到以下错误:

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 macros type-erasure scala-macros


    【解决方案1】:

    您的情况与最近的一个问题中描述的情况非常接近:scala macros: defer type inference。

    就目前情况而言,您必须将log 转换为宏。另一种方法是将Metric.jsFields 和Metric.jsValues 转换为JsFieldable 和JsValuable 类型类,由log (http://docs.scala-lang.org/overviews/macros/implicits.html) 调用点处的隐式宏实现。

    【讨论】:

    • 我尝试了您的替代方案,因为总体而言它似乎更清洁,但后来我遇到了另一个问题。请在我的编辑中查看更新。
    • 实际上,这是一个快速修复——见结论。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-30
    • 2012-05-28
    • 1970-01-01
    • 2020-07-27
    • 2012-08-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多