Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala Advanced Type Usage

The Background

I'm working on an event library in Scala. In my library you can define events like this:

val e1 = new ImperativeEvent[Int]

You can trigger them like this:

e1(42)

You can create reactions like this:

val r1 = (i: Int) => println(i)

And attach them to the event like this:

e1 += r1

There is also some other stuff (like event transformations, compositions etc). I use the Esper CEP engine as the backend of my library. Esper uses an SQL-like language called EPL for most operations.

The Problem

I'm trying to implement some more advanced concepts like event joins. So now you can define events with multiple properties like this (using tuple types):

val e2 = new ImperativeEvent[(Int, String)]

And then join them like this:

val e3 = e1 join e2 windowLength (30) on "E1.P1 = E2.P1"

which performs a join of e1 and e2 on the last 30 occurrances of both on the condition that their respective first properties are equal.

This is alright but I'd like to get rid of the strings in my implementation to make the event expressions type checkable. I'd like to change the join expression to something like this:

val e3 = e1 join e2 windowLength (30) on e1._1 === e2._1

similar to the way it is done in eg. Squeryl. The problem with this is, I can't access the types of the elements of the tuple type...

The Question

How can I access the tuple types statically? Right now I've only managed to access them at run-time through reflection which does not help me. I'm pretty sure that what I want to achieve is not possible with tuples but I'm wondering if using HLists from the shapeless library or something similar might help in achieving my goal.

like image 474
Mark Goldenstein Avatar asked Sep 11 '13 18:09

Mark Goldenstein


People also ask

What does <: mean in Scala?

It means an abstract type member is defined (inside some context, e.g. a trait or class), so that concrete implementations of that context must define that type.

What is classOf in Scala?

A classOf[T] is a value of type Class[T] . In other words, classOf[T]: Class[T] . For example: scala> val strClass = classOf[String] strClass: Class[String] = class java. lang. String scala> :t strClass Class[String]

How does Scala determine types when they are not specified?

For example, a type constructor does not directly specify a type of values. However, when a type constructor is applied to the correct type arguments, it yields a first-order type, which may be a value type. Non-value types are expressed indirectly in Scala.

What do you call a Scala method that is parameterized by type as well as by value?

Language. Methods in Scala can be parameterized by type as well as by value. The syntax is similar to that of generic classes.


1 Answers

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.

like image 54
Aaron Novstrup Avatar answered Oct 02 '22 14:10

Aaron Novstrup