【问题标题】:Scala macros Type or Symbol liftedScala 宏类型或符号已解除
【发布时间】:2015-09-16 22:29:01
【问题描述】:

我正在尝试在宏中使用类型的字段,并将字段的符号或类型符号传递给方法,以便我可以执行一些需要有关该类型的具体信息的操作。

我有这样的代码(玩弄变体):

object Macros {

  import scala.reflect.runtime.universe._

  def foo(t: Symbol) : String = t.name.decoded

  def materializeWriterImpl[T: c.WeakTypeTag](c: Context): c.Expr[List[String]] = {
    import c.universe._

    val tpe = weakTypeOf[T]

    val fields = tpe.declarations.collectFirst {
      case m: MethodSymbol if m.isPrimaryConstructor => m
    }.get.paramss.head

    c.Expr[List[String]] { q"""
      val q = $fields
      val names = q.map(Macros.foo)
      List(names)
    """
    }
  }
}

我得到的错误是

Error:(53, 24) Can't unquote List[c.universe.Symbol], consider using .. or providing an implicit instance of Liftable[List[c.universe.Symbol]] val names = foo($fields) ^

所以,也许不可能使用 qquotes 来提升符号/类型。但我可以在StandardLiftableApi 中看到执行此操作的方法:

implicit def liftScalaSymbol : U#Liftable[scala.Symbol]
implicit def liftType[T <: U#Type] : U#Liftable[T]

如果我传递字符串,我可以让它工作,就像测试一样,但我真的需要更实质性的东西传递出去。

【问题讨论】:

  • 乍一看,我想说你想要的只是 q"List(..${fields.map(Macros.foo)})"。您在这里的主要概念错误是您试图生成映射符号的代码,而不是让您生成的代码映射符号然后提升结果。作为旁注,liftScalaSymbol 用于提升scala.Symbol,而不是Universe.Symbol
  • 此外,由于编译器在错误消息中提示,当您想要插入列表时,您需要在 $ 前面加上两个点 (..)。请参阅 quasiquotes 文档。
  • 是的,我(在基本层面上)已经理解“..”了。您的第一条评论真的很有趣,我尝试将我的输出生成为 quasiquotes,然后包含 qqs 列表,但这也不起作用(同样的 Liftable 错误)。编写您赢得的 Liftable 实现是否通常?
  • 但是你试过只返回c.Expr[List[String]](q"List(..${fields.map(Macros.foo)})")吗?它有效吗?如果不是,那么确切的错误是什么?或者,如果您需要做的不仅仅是获取字段名称,它究竟是什么?
  • 我想递归生成一个模式,因此我需要获取字段名称和一些类型信息。所以理想情况下我想传递一个符号,或者一个名字+类型。

标签: scala macros scala-macros


【解决方案1】:

根据您的 cmets 了解您实际想要实现的目标,这里有一个相当全面的示例,展示了如何生成模式(对于完全组成的模式描述语言)。

import scala.language.experimental.macros
import scala.reflect.macros._

implicit class Schema[T](val value: String) extends AnyVal {
  override def toString = value
}; object Schema {
  implicit val intSchema: Schema[Int] = "int"
  implicit val floatSchema: Schema[Float] = "float"
  implicit val stringSchema: Schema[String] = "string"
  // ... and so on for all the base types you need to support

  implicit def optionSchema[T:Schema]: Schema[Option[T]] = "optional[" + implicitly[Schema[T]] + "]"
  implicit def listSchema[T:Schema]: Schema[List[T]] = "list_of[" + implicitly[Schema[T]] + "]"
  implicit def classSchema[T]: Schema[T] = macro classSchema_impl[T]

  def classSchema_impl[T:c.WeakTypeTag](c: Context): c.Expr[Schema[T]] = {
    import c.universe._
    val T = weakTypeOf[T]
    val fields = T.declarations.collectFirst {
      case m: MethodSymbol if m.isPrimaryConstructor => m
    }.get.paramss.head
    val fieldSchemaPartTrees: Seq[Tree] = fields.map{ f =>
      q"""${f.name.decoded} + ": " + implicitly[Schema[${f.typeSignature}]]"""
    }
    c.Expr[Schema[T]](q"""
      new Schema[$T](
        "{" +
        Seq(..$fieldSchemaPartTrees).mkString(", ") +
        "}"
      )
    """)
  }
}

还有一些 REPL 测试:

scala> case class Foo(ab: Option[String], cd: Int)
defined class Foo

scala> case class Bar(ef: List[Foo], gh: Float)
defined class Bar

scala> implicitly[Schema[Bar]]
res7: Schema[Bar] = {ef: list_of[{ab: optional[string], cd: int}], gh: float}

【讨论】:

  • 这太棒了,感谢您抽出宝贵时间
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-16
  • 2021-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-24
  • 1970-01-01
相关资源
最近更新 更多