【问题标题】:Scala Macros: Accessing members with quasiquotesScala 宏:使用准引号访问成员
【发布时间】:2013-10-23 14:37:57
【问题描述】:

我正在尝试实现一个隐式实现器,如下所述:http://docs.scala-lang.org/overviews/macros/implicits.html

我决定创建一个宏,它使用准引号将案例类与String 进行转换,以进行原型设计。例如:

case class User(id: String, name: String)
val foo = User("testid", "foo")

foo 转换为文本应生成"testid foo",反之亦然。

这是我创建的简单特征及其伴随对象:

trait TextConvertible[T] {
  def convertTo(obj: T): String
  def convertFrom(text: String): T
}

object TextConvertible {
  import language.experimental.macros
  import QuasiTest.materializeTextConvertible_impl
  implicit def materializeTextConvertible[T]: TextConvertible[T] = macro materializeTextConvertible_impl[T]
}

这里是宏:

object QuasiTest {
  import reflect.macros._

  def materializeTextConvertible_impl[T: c.WeakTypeTag](c: Context): c.Expr[TextConvertible[T]] = {
    import c.universe._
    val tpe = weakTypeOf[T]

    val fields = tpe.declarations.collect {
      case field if field.isMethod && field.asMethod.isCaseAccessor => field.asMethod.accessed
    }

    val strConvertTo = fields.map {
      field => q"obj.$field"
    }.reduce[Tree] {
      case (acc, elem) => q"""$acc + " " + $elem"""
    }

    val strConvertFrom = fields.zipWithIndex map {
      case (field, index) => q"splitted($index)"
    }

    val quasi = q"""
      new TextConvertible[$tpe] {
        def convertTo(obj: $tpe) = $strConvertTo
        def convertFrom(text: String) = {
          val splitted = text.split(" ")
          new $tpe(..$strConvertFrom)
        }
      }
    """

    c.Expr[TextConvertible[T]](quasi)
  }
}

生成

{
  final class $anon extends TextConvertible[User] {
    def <init>() = {
      super.<init>();
      ()
    };
    def convertTo(obj: User) = obj.id.$plus(" ").$plus(obj.name);
    def convertFrom(text: String) = {
      val splitted = text.split(" ");
      new User(splitted(0), splitted(1))
    }
  };
  new $anon()
}

生成的代码看起来不错,但我在尝试使用宏时在编译时收到错误 value id in class User cannot be accessed in User

我怀疑我使用了错误的字段类型。我试过field.asMethod.accessed.name,但结果是def convertTo(obj: User) = obj.id .$plus(" ").$plus(obj.name );(注意idname后面的多余空格),这自然会导致错误value id is not a member of User

我做错了什么?

【问题讨论】:

    标签: scala macros scala-macros


    【解决方案1】:

    啊,发送我的问题后几乎立即就想通了。

    我换行了

    val fields = tpe.declarations.collect {
      case field if field.isMethod && field.asMethod.isCaseAccessor => field.asMethod.accessed
    }
    

    val fields = tpe.declarations.collect {
      case field if field.isMethod && field.asMethod.isCaseAccessor => field.name
    }
    

    解决了这个问题。

    【讨论】:

      【解决方案2】:

      accessed.name 获得的字段附加了一个特殊的后缀,以避免命名冲突。

      特殊的后缀是scala.reflect.api.StandardNames$TermNamesApi.LOCAL_SUFFIX_STRING,它有一个值,你猜对了,一个空格字符。

      当然,这很邪恶。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多