Suppose I have to write some GUI code as follows:
widget1.addListener(event1 =>
handle1(event1)
widget2.addListener(event2 =>
handle2(event2)
widget3.addListener(event3 => handle3(event3))
)
)
How would you write it in CPS-style using Scala continuations?
Just wanted to provide working example in addition to other answers. With Scala continuations it can look like this:
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))
}
This code actually compiles and runs, so you can try it yourself. You should receive following output:
Handling first event: Event(event for first widget)
Handling second event: Event(event for second widget)
Handling first event: Event(one more event)
If you will compile this example, then don't forget to enable continuations by providing -P:continuations:enable
argument for the Scala compiler.
The point of having continuations is the ability to use a direct style of coding, even if normally I would be forced to code in a harder-to-use way (for example in an event-driven style).
So the client code I'd wish to be able to write would be like that:
reset {
val event1 = widget1.waitForEvent()
handle1(event1)
val event2 = widget2.waitForEvent()
handle2(event2)
val event3 = widget3.waitForEvent()
handle3(event3)
}
So listener stuff would be hidden from me. But of course, the listeners would still have to be somewhere underneath. I'd hide them in the widget's waitForEvent() method (either added to the Widget class or available through an implicit conversion). The method would look like:
def waitForEvent() = shift { k =>
this.addListener(event => k(event))
k
}
This is at least at the conceptual level. To get this to work you'd need to add some type- and/or @cps annotations probably.
Here's a simple working example:
reset{
shift { (k: Unit => Unit) => widget1 addListener(handle1 _ andThen k)}
shift { (k: Unit => Unit) => widget2 addListener(handle2 _ andThen k)}
widget3 addListener(handle3 _)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With