2015-09-17 49 views
1

我正在尝试编写一个可以使用有关某个类的字段信息来生成模式的宏。例如,假设我有一个名为SchemaWriter[T]的类型类,我希望使用宏来生成实现。Scala宏升降符号或类型

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 = ... 
} 

的想法是,我的宏将吐出类似代码:

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

我可以实现宏如:

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得到了一个很好的答案,但我的实现不能局限于传递字符串,因为我需要更多的类型信息来生成模式的细节,这是我认为我的困惑所在。

+0

几乎不可能在不知道“Field”的定义的情况下提供帮助。特别是,Field的构造函数的签名是什么。一般来说,在发布问题时,您应该努力发布[最小,完整和可验证的示例](http://stackoverflow.com/help/mcve)。是的,我知道你不能真正发布一个编译的例子,因为你确实要求如何修正你的代码,但至少提供了所有必需的依赖关系。 –

+0

我将使用Schema和Field的适当定义进行更新 – monkjack

+0

已针对具体架构类型进行了更新。什么进入这个Mson模式不是关键,但如果我现在可以得到怎么了编译,其余的应该很容易。 – monkjack

回答

1

我认为你的主要困惑是你似乎没有完全得到如何处理宏。 这完全是编译时间。当您的宏运行时,它可以访问有关编译程序的信息,包括Symbol s。 宏然后生成一些代码,这些代码将成为您的程序的一部分,但生成的代码本身并未获取对该宏访问的任何其他内容的访问权限。

所以MsonType.apply在你的例子是不会有任何用处的,因为输入是Symbol这是在宏观只能(在编译时), 和输出是MsonSchema,你需要在运行。是什么让感的返回类型更改从MsonTypec.Expr[MsonType](或简称c.universe.Tree), 或者换句话说MsonType.apply现在会采取一个符号,并返回一个树代表MsonType实例 (而不是返回一个MsonType实例),然后你的宏可以调用它并将它包含在最终的树中,并返回到编译器 (编译器将在你的程序中包含在调用位置)。 在这种特殊情况下,我认为最好全部删除MsonType.apply,并在writerImpl宏中实现从Symbolc.universe.Tree的转换。

这个转换其实很简单。所有你需要构建MsonType是字段名称和Class实例,所以你去:

q"""org.bibble.MsonType(${f.name.decoded}, classOf[${f.typeSignature}])""" 

对于Bar类型的字段foo,这将产生一个树代表下面的表达式:

org.bibble.MsonType("foo", classOf[Bar]) 

就是这样,我们很好走。

您会在下面找到完整的实现。请注意,我冒昧地以一种更符合逻辑和多用途的方式更改模式类型的定义。 特别是,每个领域现在都有其相关的模式(给出您以前的问题,这显然是你想要的第一个地方)。

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()))))