Skip to content

Instantly share code, notes, and snippets.

@nafg
Created July 8, 2012 07:31
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save nafg/3069790 to your computer and use it in GitHub Desktop.
CanBuildQuery
// The problem: get the line starting with val q (at the end) to be compile as implicit and inferred as possible
/*
THE IDEA:
Suppose for instance I want to develop a very pluggable issue tracker. Its core implementation might
only support a ticket id and a description. Other extensions might add support for various other fields,
yet those fields might exist in the database on the same table.
Even if not, the number of queries to the database should not need to increase along with the number of
extensions. They should be able to contribute to the definition of the query.
These objects would also be able to render the data. So the second might know how to render a B,
while the first might be able to render only an A.
Item[A, B, R[_]] would represent a column, with A as the table type (has the column representations),
B as the data type, and R as the type constructor representing a column of type B. So R[B] might be
ScalaQuery's NamedColumn[String].
So for building up the query you go down the list of extensions, adding more to the query, and for
rendering you go up the list: each extension renders its data and passes the remaining (earlier) part
of the data up the list.
Right now I'm trying to make a typeclass to handle building the "query".
*/
object Items {
type Id[A] = A
class CanBuildQuery[A, B, R[_], Q, I <: Item[A, B, R]](val apply: I => A => Q)
trait Low {
implicit def defaultNext[A, B, R[_], B0, P <: Item[A, B0, R], I <: NextItem[A, B, B0, R, P], PQ](
implicit cbq: CanBuildQuery[A, B0, R, PQ, P]
): CanBuildQuery[A, B, R, (PQ, R[B]), I] =
new CanBuildQuery[A, B, R, (PQ, R[B]), I](
ni => a => (query(ni.prev)(cbq) apply a) -> ni.get(a)
)
}
object CanBuildQuery extends Low {
implicit def defaultFirst[A, B, R[_]]: CanBuildQuery[A, B, R, R[B], FirstItem[A, B, R]] =
new CanBuildQuery[A, B, R, R[B], FirstItem[A, B, R]](_.get)
}
def query[A, B, R[_], Q, I <: Item[A, B, R]](i: I with Item[A, B, R])(cbq: CanBuildQuery[A, B, R, Q, I]): A => Q = cbq apply i
trait Item[A, B, +R[_]] {
type O
type X = String => String //TODO
def get: A => R[B]
def render: B => X
def renderAll: O => X
}
object Item {
def apply[A, B, R[_]](get: A => R[B])(render: B => String => String) = {
val get0 = get
val render0 = render
new FirstItem[A, B, R] {
def get = get0
def render = render0
}
}
}
trait FirstItem[A, B, R[_]] extends Item[A, B, R] {
type O = B
def get: A => R[B]
def render: B => X
def renderAll = render
def &[B2](item: Item[A, B2, R]) =
new NextItem[A, B2, B, R, FirstItem[A, B, R]] {
val prev = FirstItem.this
def get = item.get
def render = item.render
}
}
trait NextItem[A, B, B0, R[_], I <: Item[A, B0, R]] extends Item[A, B, R] {
type O = (prev.O, B)
val prev: I
def get: A => R[B]
def render: B => X
def renderAll = { case (o, b) => prev.renderAll(o) andThen render(b) }
def &[B2](item: Item[A, B2, R]) =
new NextItem[A, B2, B, R, NextItem[A, B, B0, R, I]] {
val prev = NextItem.this
def get = item.get
def render = item.render
}
}
}
object Test {
def main(args: scala.Array[String]) {
import Items._
class NamedColumn[T]
object Field { val id = new NamedColumn[Long]; val name = new NamedColumn[String] }
def cat[T] = { x: T => (_: String) + " " + x }
val items =
Item((_: Field.type).id)(cat) &
Item((_: Field.type).name)(cat) /*&
Item((_: Field.type).allowMultiple)(cat)*/
val q = query(
items
)(
CanBuildQuery.defaultNext (CanBuildQuery.defaultFirst)
) apply Field
println(q)
def printType[X: Manifest](x: X) = println(manifest[X])
printType(q)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment