Scala reflection to instantiate scala.slick.lifted.TableQuery

I have this baseline

trait MyBase { type M type T <: Table[M] val query: TableQuery[T] } 

Where TableQuery is scala.slick.lifted.TableQuery

My subclasses instantiate TableQuery as follows:

 type M = Account type T = AccountsTable val query = TableQuery[T] 

I would like to create an instance of TableQuery in the underlying TableQuery , possibly using lazy val , i.e.

 lazy val query: TableQuery[T] = { ... } 

I played with reflection, but didn't have much luck.

+8
reflection scala slick
source share
1 answer

If I understand correctly, what you want is to extend MyBase by simply defining M and T , but without explicitly creating an instance of TableQuery in each derived class.

Using reflection is actually not an option, since you usually use TableQuery.apply for this (as in val query = TableQuery[MyTable] ), and this is implemented with a macro, so you have a "runtime and compilation" problem.

If you absolutely need MyBase be a sign (as opposed to a class), 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), that is, at least one solution. Since I hinted at another related question ( How to define a generic type in Scala? ), You can define a type class (say TableQueryBuilder ) to capture the call to TableQuery.apply (at the point where the particular type is known) along with an implicit macro (say TableQueryBuilder.builderForTable ) to provide an instance of this type. Then you can define a method (say TableQueryBuilder.build ) to actually create an instance of TableQuery that simply delegates the 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 no longer need to know a specific value of type T to be able to instantiate TableQuery[T] , provided that you have an implicit instance of TableQueryBuilder[T] in scope. In other words, you can transfer the need to know a specific value of T until you really know it.

MyBase (now a class) can be implemented as follows:

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

And you can extend it without having to 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 it! 

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

The reason you can't apply this template if MyBase was a MyBase is pretty mundane: MyBase constructors can't have parameters, let alone implicit parameters, so there would be no implicit way to pass an instance of TableQueryBuilder .

+8
source share

All Articles