首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Scala宏提升符号或类型

Scala宏提升符号或类型
EN

Stack Overflow用户
提问于 2015-09-17 23:07:39
回答 1查看 1K关注 0票数 1

我试图编写一个宏,该宏可以使用有关类的字段的信息来生成模式。例如,假设我有一个名为SchemaWriter[T]的类型类型,我希望使用宏为其生成实现。

代码语言:javascript
复制
trait SchemaWriter[T] {
  def schema: org.bibble.MsonSchema
}

case class MsonSchema(fields: Seq[MsonType])
case class MsonType(name: String, `type`: Class[_]) // might want other stuff here too, derived from a symbol or type-signature
object MsonType {
  def apply(symbol:Symbol): MsonType = ... 
}

这样做的想法是,我的宏将输出类似于:

代码语言:javascript
复制
class FooSchemaWriter extends SchemaWriter[Foo] {
  def schema : org.bibble.MsonSchema= {
   val fields = for (symbol <- fields of class) yield {
    MsonType(symbol)
   }
   org.bibble.MsonSchema(fields)
 }
}

我可以实现宏,例如:

代码语言:javascript
复制
object Macros {

  def writerImpl[T: c.WeakTypeTag](c: Context): c.Expr[SchemaWriter[T]] = {

    import c.universe._
    val T = weakTypeOf[T]

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

    val fieldTrees: [Tree] = fields.map { f =>
      q"""org.bibble.MsonType(${f})"""
    }

    c.Expr[SchemaWriter[T]]( q"""
      new SchemaWriter[$T] {
        def schema = {
         val fields = Seq(..$fieldTrees)
         org.bibble.MsonSchema(fields)
        }
      }
    """)
  }
}

但是,为字段创建qqs会导致一个可提升的错误或一个引用错误。昨天我问了一个类似的问题,Scala macros Type or Symbol lifted得到了一个很棒的答案,但是我的实现不能局限于传递String,因为我需要更多的类型信息来生成模式的细节,这就是我认为我的困惑所在。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-09-25 12:49:27

我认为您主要的困惑是,您似乎并不完全了解宏是如何处理的。这完全是编译时间。当宏运行时,它可以访问有关已编译程序的信息,包括Symbol。然后宏生成一些代码,这些代码将成为程序的一部分,但是生成的代码本身不能对Symbol或宏可以访问的任何其他代码进行更多的访问。

因此,在您的示例中,MsonType.apply将没有任何用处,因为输入是一个仅在宏中可用的Symbol (在编译时),而输出是运行时需要的MsonSchema。有意义的是将返回类型从MsonType更改为c.Expr[MsonType] (或简单地更改为c.universe.Tree),或者换句话说,MsonType.apply现在将获取一个符号并返回表示实例树(而不是返回MsonType实例),然后宏可以调用它,并将其包含在返回给编译器的最终树中(编译器随后将其包含在调用站点的程序中)。在这种情况下,我认为最好完全删除MsonType.apply,并在Symbol宏中实现从Symbolc.universe.Tree的转换。

这种转换实际上非常容易。构建MsonType所需的只是字段名和一个Class实例,所以您可以这样做:

代码语言:javascript
复制
q"""org.bibble.MsonType(${f.name.decoded}, classOf[${f.typeSignature}])"""

对于类型为foo的字段Bar,这将生成一棵树,表示以下表达式:

代码语言:javascript
复制
org.bibble.MsonType("foo", classOf[Bar])

就这样,我们可以走了。

您将在下面找到一个完整的实现。请注意,我已经冒昧地更改了模式类型的定义,这种方式对我来说更加合理和通用。特别是,每个字段现在都有其相关联的模式(考虑到前面的问题,这显然是您首先想要的)。

代码语言:javascript
复制
scala> :paste
// Entering paste mode (ctrl-D to finish)

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

case class MsonSchema(`type`: Class[_], fields: Seq[MsonField])
case class MsonField(name: String, schema: MsonSchema)
trait SchemaWriter[T] {
  def schema: MsonSchema
}
object SchemaWriter {
  def apply[T:SchemaWriter]: SchemaWriter[T] = implicitly
  implicit def defaultWriter[T] = macro Macros.writerImpl[T]
}
object Macros {
  def writerImpl[T: c.WeakTypeTag](c: Context): c.Expr[SchemaWriter[T]] = {
    import c.universe._
    c.Expr[SchemaWriter[T]](generateSchemaWriterTree(c)(weakTypeOf[T]))
  }
  private def generateSchemaWriterTree(c: Context)(T: c.universe.Type): c.universe.Tree = {
    import c.universe._
    val fields = T.declarations.collectFirst {
      case m: MethodSymbol if m.isPrimaryConstructor => m
    }.get.paramss.head

    val MsonFieldSym = typeOf[MsonField].typeSymbol
    val SchemaWriterSym = typeOf[SchemaWriter[_]].typeSymbol
    val fieldTrees: Seq[Tree] = fields.map { f =>
      q"""new $MsonFieldSym(
        ${f.name.decoded},
        _root_.scala.Predef.implicitly[$SchemaWriterSym[${f.typeSignature}]].schema
      )"""
    }

    c.resetLocalAttrs(q"""
      new $SchemaWriterSym[$T] {
        val schema = MsonSchema(classOf[$T], Seq(..$fieldTrees))
      }
    """)
  }
}

// Exiting paste mode, now interpreting.

warning: there were 7 deprecation warnings; re-run with -deprecation for details
warning: there was one feature warning; re-run with -feature for details
import scala.language.experimental.macros
import scala.reflect.macros._
defined class MsonSchema
defined class MsonField
defined trait SchemaWriter
defined object SchemaWriter
defined object Macros

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

scala> SchemaWriter[Foo].schema
res0: MsonSchema = MsonSchema(class Foo,List(MsonField(ab,MsonSchema(class java.lang.String,List())), MsonField(cd,MsonSchema(int,List()))))
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32641337

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档