Scala宏,生成类型参数调用

时间:2013-06-03 13:37:12

标签: scala generics macros scala-macros squeryl

我正在试图概括设置Squeryl(Slick带来同样的问题AFAIK)。我想避免为许多通用方法明确命名每个案例类。

table[Person]
table[Bookmark]
etc.

这也适用于生成索引,并为每个案例类创建围绕CRUD方法的包装器方法。

理想情况下,我想要做的是拥有一个类列表并将它们放入表中,添加索引并添加包装器方法:

val listOfClasses = List(classOf[Person], classOf[Bookmark])
listOfClasses.foreach(clazz => {
  val tbl = table[clazz]
  tbl.id is indexed
  etc.
})

我认为Scala Macros会适用于此处,因为我认为您不能将值作为类型参数。此外,我需要为每种类型的表单生成方法:

def insert(model: Person): Person = persons.insert(model)

我在Macros上有一个例子,但我不知道如何生成通用数据结构。

我有一个简单的例子来说明我想要的东西:

def makeList_impl(c: Context)(clazz: c.Expr[Class[_]]): c.Expr[Unit] = {
  import c.universe._

  reify {
    println(List[clazz.splice]()) // ERROR: error: type splice is not a member of c.Expr[Class[_]]
  }
}

def makeList(clazz: Class[_]): Unit = macro makeList_impl

我该怎么做?或者Scala Macros是错误的工具吗?

2 个答案:

答案 0 :(得分:3)

不幸的是,reify对于您的用例来说不够灵活,但有好消息。在宏观天堂(最有可能在2.11.0),我们有一个更好的工具来构建树,称为quasiquotes:http://docs.scala-lang.org/overviews/macros/quasiquotes.html

scala> def makeList_impl(c: Context)(clazz: c.Expr[Class[_]]): c.Expr[Any] = {
     | import c.universe._
     | val ConstantType(Constant(tpe: Type)) = clazz.tree.tpe
     | c.Expr[Any](q"List[$tpe]()")
     | }
makeList_impl: (c: scala.reflect.macros.Context)(clazz: c.Expr[Class[_]])c.Expr[Any]

scala> def makeList(clazz: Class[_]): Any = macro makeList_impl
defined term macro makeList: (clazz: Class[_])Any

scala> makeList(classOf[Int])
res2: List[Int] = List()

scala> makeList(classOf[String])
res3: List[String] = List()

Quosequotes甚至可以在2.10.x中使用,只需对构建过程进行一些小调整(http://docs.scala-lang.org/overviews/macros/paradise.html#macro_paradise_for_210x),因此您可能需要尝试一下。

答案 1 :(得分:1)

这可能不会满足您的所有需求,但它可能会有所帮助:

table方法的签名如下所示:

protected def table[T]()(implicit manifestT: Manifest[T]): Table[T]

如您所见,它采用隐式Manifest对象。该对象由编译器自动传递,并包含有关类型T的信息。这实际上是Squeryl用来检查数据库实体类型的。

你可以像这样明确地传递这些清单:

val listOfManifests = List(manifest[Person], manifest[Bookmark])
listOfManifests.foreach(manifest => {
  val tbl = table()(manifest)
  tbl.id is indexed
  etc.
})

不幸的是,此代码中的tbl类型与Table[_ <: CommonSupertypeOfAllGivenEntities]类似,这意味着它上面的所有操作都必须与具体类型的数据库实体无关。