Scala reflection to instantiate scala.slick.lifted.TableQuery -
i have base trait
trait mybase { type m type t <: table[m] val query: tablequery[t] }
where tablequery
scala.slick.lifted.tablequery
my subclasses instantiate tablequery
so:
type m = account type t = accountstable val query = tablequery[t]
i'd instantiate tablequery
in base trait, possibly using lazy val
, i.e.
lazy val query: tablequery[t] = { ... }
i've been playing around reflection, haven't had luck.
if understand correctly, want able extend mybase
defining m
, t
without having explicitly instantiate tablequery
in each derived class.
using reflection not option because use tablequery.apply
(as in val query = tablequery[mytable]
), , implemented through macro, you've got "runtime vs compile-time" issue.
if absolutely need mybase
trait (as opposed class), don't see viable solution. if can turn mybase
class and turn m
, t
type parameters (instead of abstract types), there @ least 1 solution. hinted in related question (how define generic type in scala?), can define type class (say tablequerybuilder
) capture call tablequery.apply
(at point concrete type known) along implicit macro (say tablequerybuilder.builderfortable
) provide instance of type class. can define method (say tablequerybuilder.build
) instantiate tablequery
, delegate job type class.
// note: tested 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 don't need anymore know concrete value of type t
in order able instantiate tablequery[t]
, provided have implicit instance of tablequerybuilder[t]
in scope. in other words, can shift need know concrete value of t
point know it.
mybase
(now class) can implemented this:
class mybase[m, t <: table[m] : tablequerybuilder] { lazy val query: tablequery[t] = tablequerybuilder.build[t] }
and can extend without need 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 in derived
's constructor, implicit value tablequerybuilder[coffees]
implicitly passed mybase
's constructor.
the reason why cannot apply pattern if mybase
trait pretty mundane: trait constructors cannot have parameters, let alone implicit parameters, there no implicit way pass tablequerybuilder
instance.
Comments
Post a Comment