使用字符串值scala宏的对象的引用字段

时间:2017-01-04 14:48:14

标签: scala macros

我试图编写一个宏,将一个案例类的给定实例扩展为字符串表示。例如。 case class Foo(a: Int); Foo(1)将成为a -> 1

所以我写了一个类型类来给我一个名为LabelledGeneric的无形FieldList字段名称。我将LabelledGeneric和FieldList实例传递给我的宏,我可以轻松地使宏生成所有字段的列表。但是,我不确定如何使用表示字段的字符串从我的宏体中的对象访问该字段。这是宏代码:

import FieldList._
import shapeless.{HList, LabelledGeneric}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox

object Foo {

  def foo_impl[T, L <: HList](c: blackbox.Context)
                                 (t: c.Expr[T])
                                 (gen: c.Expr[LabelledGeneric.Aux[T, L]],
                                  fl: c.Expr[FieldList[L]]): c.Expr[String] = {
    import c.universe._
    reify {
      val sb = new StringBuilder
      val obj = t.splice
      val generic = gen.splice
      val fieldList = fl.splice
      // T.fieldList returns a List[String] of the class' fields. 
      obj.fieldList(generic, fieldList).foldLeft(sb) { case (builder, next) =>
        builder.append(next)
        builder.append(" -> ")
        builder.append() // How to get the value of obj.$next?
      }.toString()
    }
  }

  def foo[T, L <: HList](t: T)(implicit gen: LabelledGeneric.Aux[T, L], fl: FieldList[L]): String = macro foo_impl[T, L]

}

我不知道该怎么做的一行是我评论的第三行。

我确定我想要做的事情是无形的,但我正在尝试学习宏。我查看了quasiquotes,看起来他们可以支持这种行为,但看起来我必须在reify和quasiquotes和AFAICT之间做出选择我只能在reify块中访问Expr的值(所以quasiquotes不会工作?)。

1 个答案:

答案 0 :(得分:1)

使用quasiquotes完全可以拼接。只有你拼接Tree,而不是Expr(你可以ExprTree expr.tree。这是一个对代码进行最小更改的解决方案:

import FieldList._
import shapeless.{HList, LabelledGeneric}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox

object Foo {

  def foo_impl[T, L <: HList](c: blackbox.Context)
                                 (t: c.Tree)
                                 (gen: c.Tree,
                                  fl: c.Tree): c.Tree = {
    import c.universe._
    val q"$_(..$args)" = t
    q"""
      val sb = new _root_.scala.StringBuilder
      val obj = $t
      val generic = $gen
      val fieldList = $fl
      val argsList = _root_.scala.List(..$args)
      // T.fieldList returns a List[String] of the class' fields. 
      obj.fieldList(generic, fieldList)
         .zip(argsList)
         .foldLeft(sb) { case (builder, (next, value)) =>
        builder.append(next)
        builder.append(" -> ")
        builder.append(value)
      }.toString()
    """
  }

  def foo[T, L <: HList](t: T)(implicit gen: LabelledGeneric.Aux[T, L], fl: FieldList[L]): String = macro foo_impl[T, L]

}

请注意我没有编译或测试过这个,因为我没有其他代码依赖于此。如果这确实有效,它可能不适用于更复杂的情况,如foo(Foo(b = "bar", a = 1))。但是它可能会让你对如何使用quasiquotes有一个大概的了解。