Scala reflection to instantiate scala.slick.lifted.TableQuery

reflection, scala, slick

Solution

If I understand correctly, what you want is to be able to extend `MyBase` by simply defining `M` and `T` but without having to explicitly instantiate the `TableQuery` in each derived class.

Using reflection is not really an option because normally you use `TableQuery.apply` for that (as in `val query = TableQuery[MyTable]`), and this is implemented through a macro, so you've got a "runtime vs compile-time" issue.

If you absolutely need `MyBase` to be a trait (as opposed to a class), then I don't see any viable solution. However if you can turn `MyBase` into a class and turn `M` and `T` into type parameters (instead of abstract types), then there is at least one solution. As I hinted in another related question (How to define generic type in Scala?), you can define a type class (say `TableQueryBuilder`) to capture the call to `TableQuery.apply` (at the point where the concrete type is known) along with an implicit macro (say `TableQueryBuilder.builderForTable`) to provide an instance of this type class. You can then define a method (say `TableQueryBuilder.build`) to actually instantiate the `TableQuery`, which will just delegate to job to the type class.

// NOTE: tested with scala 2.11.0 & slick 3.0.0
import scala.reflect.macros.Context
import scala.language.experimental.macros
object TableQueryBuilderMacro {
  def createBuilderImpl[T<:AbstractTable[_]:c.WeakTypeTag](c: Context) = {
    import c.universe._
    val T = weakTypeOf[T]
    q"""new TableQueryBuilder[$T]{
      def apply(): TableQuery[$T] = {
        TableQuery[$T]
      }
    }"""
  }
}
trait TableQueryBuilder[T<:AbstractTable[_]] {
  def apply(): TableQuery[T]
}
object TableQueryBuilder {
  implicit def builderForTable[T<:AbstractTable[_]]: TableQueryBuilder[T] = macro TableQueryBuilderMacro.createBuilderImpl[T]
  def build[T<:AbstractTable[_]:TableQueryBuilder](): TableQuery[T] = implicitly[TableQueryBuilder[T]].apply()
}

The net effect is that you don't need anymore to know the concrete value of the type `T` in order to be able to instantiate a `TableQuery[T]`, provided that you have an implicit instance of `TableQueryBuilder[T]` in scope. In other words, you can shift the need to know the concrete value of `T` up to the point where you actually know it.

`MyBase` (now a class) can then be implemented like this:

class MyBase[M, T <: Table[M] : TableQueryBuilder] {
  lazy val query: TableQuery[T] = TableQueryBuilder.build[T]
}

And you can then extend it without the need to explcitly call `TableQuery.apply`:

class Coffees(tag: Tag) extends Table[(String, Double)](tag, "COFFEES") {
  def name = column[String]("COF_NAME")
  def price = column[Double]("PRICE")
  def * = (name, price)
}

class Derived extends MyBase[(String, Double), Coffees] // That's it!

What happens here is that in `Derived`'s constructor, an implicit value for `TableQueryBuilder[Coffees]` is implicitly passed to `MyBase`'s constructor.

The reason why you cannot apply this pattern if `MyBase` were a trait is pretty mundane: trait constructors cannot have parameters, let alone implicit parameters, so there would be no implicit way to pass the `TableQueryBuilder` instance.

Problem

I have this base trait ``` trait MyBase { type M type T <: Table[M] val query: TableQuery[T] } ``` Where `TableQuery` is `scala.slick.lifted.TableQuery` My subclasses instantiate `TableQuery` like so: ``` type M = Account type T = AccountsTable val query = TableQuery[T] ``` I'd like to instantiate the `TableQuery` in the base trait, possibly by using a `lazy val`, i.e. ``` lazy val query: TableQuery[T] = { ... } ``` I've been playing around with reflection, but haven't had much luck.

Original source

Related problems