Search code examples
scalagenericsmacrosscala-macrossqueryl

Scala Macros, generating type parameter calls


I'm trying to generalize setting up Squeryl (Slick poses the same problems AFAIK). I want to avoid having to name every case class explicitly for a number of general methods.

table[Person]
table[Bookmark]
etc.

This also goes for generating indexes, and creating wrapper methods around the CRUD methods for every case class.

So ideally what I want to do is have a list of classes and make them into tables, add indexes and add a wrapper method:

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

I thought Scala Macros would be the thing to apply here, since I don't think you can have values as type parameters. Also I need to generate methods for every type of the form:

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

I've got my mits on an example on Macros but I don't know how to generate a generic datastructure.

I got this simple example to illustrate what I want:

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

How do I do this? Or is Scala Macros the wrong tool?


Solution

  • Unfortunately, reify is not flexible enough for your use case, but there's good news. In macro paradise (and most likely in 2.11.0) we have a better tool to construct trees, called 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()
    

    Quasiquotes are even available in 2.10.x with a minor tweak to the build process (http://docs.scala-lang.org/overviews/macros/paradise.html#macro_paradise_for_210x), so you might want to give them a try.