假设我必须编写一些GUI代码,如下所示:
widget1.addListener(event1 =>
handle1(event1)
widget2.addListener(event2 =>
handle2(event2)
widget3.addListener(event3 => handle3(event3))
)
)
您如何使用Scala延续以CPS风格编写?
答案 0 :(得分:8)
除了其他答案之外,只想提供工作示例。使用Scala continuation,它看起来像这样:
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
参数来启用continuation。
答案 1 :(得分:3)
持续性的关键是使用直接编码风格的能力,即使通常我会被迫以更难以使用的方式编码(例如以事件驱动的方式)。
所以我希望能够编写的客户端代码就是这样:
reset {
val event1 = widget1.waitForEvent()
handle1(event1)
val event2 = widget2.waitForEvent()
handle2(event2)
val event3 = widget3.waitForEvent()
handle3(event3)
}
所以听众的东西会对我隐瞒。但是,当然,听众仍然必须在某个地方。我将它们隐藏在widget的waitForEvent()方法中(要么添加到Widget类,要么通过隐式转换提供)。该方法如下所示:
def waitForEvent() = shift { k =>
this.addListener(event => k(event))
k
}
至少在概念层面。要使其工作,您可能需要添加一些类型和/或@cps注释。
答案 2 :(得分:1)
这是一个简单的工作示例:
reset{
shift { (k: Unit => Unit) => widget1 addListener(handle1 _ andThen k)}
shift { (k: Unit => Unit) => widget2 addListener(handle2 _ andThen k)}
widget3 addListener(handle3 _)
}