Created
July 8, 2012 07:31
-
-
Save nafg/3069790 to your computer and use it in GitHub Desktop.
CanBuildQuery
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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