Without more details on your DSL, I'm afraid it's not clear what you mean by "access the tuple types statically". Here's a simplified version of the API that has no trouble with tuple types:
class Event[T] {
def joinOn[T2, R](ev2: Event[T2])(f: (T, T2) => R) = new Event[R]
}
You can use this as follows:
val e1 = new Event[(Int, String)]
val e2 = new Event[(Int, String)]
val e3 = e1.joinOn(e2)(_._1 == _._2)
It should be easy to see how this could be extended to supporting your join/windowLength/on syntax.
Update:
I can see that your use case is complicated by the fact that you need to translate the Scala-encoded query expression to another query language. In this case, you want the on
method's signature to look like:
def on[T2, R](f: (Expr[T], Expr[T2]) => Expr[R]): Event[R]
Internally, each event object would create its own Expr representation and would pass this representation into the function supplied to the on
method.
The Expr
type could be defined like:
trait Expr[T] {
protected val repr: String
def _1[A](implicit ev: T <:< Tuple2[A,_]): Expr[A] =
??? // create an Expr[A] whose string representation is (repr + ".P1")
// abstracting over tuple arities (using Shapeless)
import shapeless._, nat._
@scala.annotation.implicitNotFound("A tuple with at least 3 elements is required")
type At2 = ops.tuple.At[T, _2]
def _3(implicit at: At2): Expr[at.Out] =
??? // create an Expr[at.Out] whose string representation is (repr + ".P3")
def ===(other: Expr[T]): Expr[Boolean] =
??? // create an Expr[T] whose string representation is (repr + " = " + other.repr)
}
This is obviously dramatically simplified but should help to get you started.