Question

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?

Was it helpful?

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.

OTHER TIPS

This will probably not fill all your needs here, but it may help a bit:

The signature of table method looks like this:

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

As you can see, it takes implicit Manifest object. That object is passed automatically by the compiler and contains information about type T. This is actually what Squeryl uses to inspect database entity type.

You can just pass these manifests explicitly like this:

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

Unfortunately tbl in this code will have type similar to Table[_ <: CommonSupertypeOfAllGivenEntities] which means that all operations on it must be agnostic of concrete type of database entity.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top