我正在使用Scala中的事件库。 In my library您可以定义以下事件:
val e1 = new ImperativeEvent[Int]
你可以像这样触发它们:
e1(42)
你可以创建这样的反应:
val r1 = (i: Int) => println(i)
并将它们附加到这样的事件:
e1 += r1
还有其他一些东西(比如事件转换,组合等)。我使用Esper CEP engine作为我的库的后端。对于大多数操作,Esper使用类似SQL的语言EPL。
我正在尝试实现一些更高级的概念,比如事件连接。所以现在你可以用这样的多个属性定义事件(使用元组类型):
val e2 = new ImperativeEvent[(Int, String)]
然后像这样加入他们:
val e3 = e1 join e2 windowLength (30) on "E1.P1 = E2.P1"
在它们各自的第一个属性相等的条件下,对两者的最后30次出现执行e1和e2的连接。
这没关系,但我想摆脱我的实现中的字符串,使事件表达式类型可检查。我想将连接表达式更改为:
val e3 = e1 join e2 windowLength (30) on e1._1 === e2._1
类似于在例如中完成的方式。 Squeryl。这个问题是,我无法访问元组类型元素的类型......
如何静态访问元组类型?现在我只能通过反射在运行时访问它们,这对我没有帮助。我很确定我想要实现的是元组不可能实现的,但我想知道使用无形库中的HLists或类似的东西是否有助于实现我的目标。
答案 0 :(得分:4)
如果没有关于DSL的更多细节,我担心“静态访问元组类型”并不清楚你的意思。这是API的简化版本,对元组类型没有任何问题:
class Event[T] {
def joinOn[T2, R](ev2: Event[T2])(f: (T, T2) => R) = new Event[R]
}
您可以按如下方式使用:
val e1 = new Event[(Int, String)]
val e2 = new Event[(Int, String)]
val e3 = e1.joinOn(e2)(_._1 == _._2)
应该很容易看出如何将其扩展到支持join / windowLength / on语法。
<强>更新强>
我可以看到您的用例很复杂,因为您需要将Scala编码的查询表达式转换为另一种查询语言。在这种情况下,您希望on
方法的签名如下所示:
def on[T2, R](f: (Expr[T], Expr[T2]) => Expr[R]): Event[R]
在内部,每个事件对象都会创建自己的Expr表示,并将此表示传递给提供给on
方法的函数。
Expr
类型可以定义为:
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)
}
这显然已经大大简化了,但应该有助于您入门。
答案 1 :(得分:0)
予。有一个SynapseGrid功能反应式编程库。在消息来源中,您可以找到一些有用的提示。
库中的所有处理都是类型安全的。您可以完全访问元组。
例如,如果我必须在SynapseGrid中实现连接,我将定义以下签名的方法join
:
implicit class RichContact[T] (c:Contact[T]){ // contact == event in SynapseGrid's terminology
...
def join[T2](c2:Contact[T2]):Contact[(T, T2)] = {
// construct a contact/event that do nothing more than joining two events.
}
}
implicit class RichTupledContact[T, T2](c:Contact[(T, T2)])
def windowLength(len:Int):Contact[(T, T2)] = {
// construct the next step of processing events — window
}
}
等等。逐步建立事件处理大大简化了系统的构建。
II。但是,如果您需要一次构造所有内容,那么您可能会返回一些具有构造方法的中间对象:
implicit class RichContact[T] (c:Contact[T]){ // contact == event in SynapseGrid's terminology
...
def join[T2](c2:Contact[T2]):Contact[(T, T2)] = {
new {
def windowLength(len:Int) = ...
}
}
}