【问题标题】:Reference field of object using string value scala macro使用字符串值scala宏的对象引用字段
【发布时间】:2017-01-04 14:48:13
【问题描述】:

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

所以我编写了一个类型类来为我提供来自名为FieldList 的无形LabelledGeneric 的字段名称。我将 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]

}

我不确定该做什么是我折叠的第 3 行带有注释的行。

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

【问题讨论】:

    标签: scala macros


    【解决方案1】:

    使用准引号完全可以进行拼接。只有你拼接 Tree,而不是 Expr(你可以从 Expr 转到 Treeexpr.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。

    【讨论】:

    • @rahilb 顺便说一句,当我使用自己的虚拟 FieldList 时,这对我有用。
    • 谢谢 Jasper:我会接受你的回答,但我意识到我实际上问错了问题!出于我的目的,我需要从输入树中提取无形类型标签,因为我想在编译时知道字段名称(我仍在为此苦苦挣扎,但那是另一回事:))。再次感谢。
    • @rahilb 我猜你可能最好完全无形或完全使用宏来做这件事。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-17
    • 2014-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多