带有Scala continuations的事件侦听器

带有Scala continuations的事件侦听器,scala,user-interface,continuations,continuation-passing,Scala,User Interface,Continuations,Continuation Passing,假设我必须编写一些GUI代码,如下所示: widget1.addListener(event1 => handle1(event1) widget2.addListener(event2 => handle2(event2) widget3.addListener(event3 => handle3(event3)) ) ) 您将如何使用Scala continuations以CPS样式编写它 下面是一个简单的工作示例: reset{ shift { (

假设我必须编写一些GUI代码,如下所示:

widget1.addListener(event1 =>
   handle1(event1)
   widget2.addListener(event2 =>
     handle2(event2)
     widget3.addListener(event3 => handle3(event3))
   )
)

您将如何使用Scala continuations以CPS样式编写它

下面是一个简单的工作示例:

reset{
  shift { (k: Unit => Unit) => widget1 addListener(handle1 _ andThen k)}
  shift { (k: Unit => Unit) => widget2 addListener(handle2 _ andThen k)}
  widget3 addListener(handle3 _)
}

延续的意义在于能够使用直接的编码风格,即使通常我会被迫以更难使用的方式(例如,以事件驱动的方式)编码

因此,我希望能够编写的客户端代码如下:

reset {
    val event1 = widget1.waitForEvent()
    handle1(event1)
    val event2 = widget2.waitForEvent()
    handle2(event2)
    val event3 = widget3.waitForEvent()
    handle3(event3)
}
所以听众的东西会对我隐瞒。但当然,听众仍然必须在下面的某个地方。我将它们隐藏在小部件的waitForEvent()方法中(可以添加到小部件类中,也可以通过隐式转换获得)。方法如下所示:

def waitForEvent() = shift { k =>
    this.addListener(event => k(event))
    k
}

这至少是在概念层面上。要使这一点起作用,您可能需要添加一些类型和/或@cps注释。

只是想在其他答案之外提供工作示例。对于Scala continuations,它可以如下所示:

import scala.util.continuations._

object ContinuationsExample extends App {
  val widget1 = Widget()
  val widget2 = Widget()

  reset {
    val event1 = widget1.getEvent
    println("Handling first event: " + event1)
    val event2 = widget2.getEvent
    println("Handling second event: " + event2)
  }

  widget2 fireEvent Event("should not be handled")
  widget1 fireEvent Event("event for first widget")
  widget2 fireEvent Event("event for second widget")
  widget1 fireEvent Event("one more event")
}

case class Event(text: String)

case class Widget() {
  type Listener = Event => Unit
  var listeners : List[Listener] = Nil

  def getEvent = shift { (l: Listener) =>
    listeners = l +: listeners
  }

  def fireEvent(event: Event) = listeners.foreach(_(event))
}
这段代码实际上是编译和运行的,所以您可以自己尝试。您应该会收到以下输出:

Handling first event: Event(event for first widget)
Handling second event: Event(event for second widget)
Handling first event: Event(one more event) 

如果要编译此示例,请不要忘记通过为Scala编译器提供
-p:continuations:enable
参数来启用continuations

您不需要类似于
shift{k=>handle1(event1);k}
的东西吗?还要注意,这里的重置实际上是多余的,因为实际上并不是从它内部调用shift,尽管它一开始看起来可能是这样的。在这个例子中,任何想运行事件处理函数的人都会调用shift,但没有一行这样做。感谢您的建议,我已经修改了代码段。实际上,这是编译器插件,它将代码转换为CPS形式。您的代码应该以直接风格编写——这就是Scala延续支持的目的。我想这就是您的想法?每次事件1发生时,该代码都会向widget2添加一个新的侦听器。因此,在第四次,widget2将向widget3添加三个侦听器!