2011-05-19 38 views
5

假設我有如下寫一些GUI代碼:使用Scala事件偵聽器延長部

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

你怎麼會寫它使用Scala的延續CPS風格?

+0

實際上,這是編譯器插件,它將您的代碼轉換爲CPS格式。你的代碼應該是直接編寫的 - 這就是Scala延續支持的用途。我想這就是你的想法? – 2011-05-19 20:54:47

+0

每次發生event1時,該代碼都會向widget2添加新的偵聽器。因此,第四次,widget2將向widget3添加三個監聽器! – nafg 2012-01-06 09:40:22

回答

8

只是想提供工作的例子,除了其他答案。隨着斯卡拉延續它可以看起來像這樣:

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參數,允許延續。

1

這裏有一個簡單的工作示例:

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

你不需要'shift {k => handle1(event1); k}'? – 2011-05-19 20:42:44

+1

另外請注意,這裏的重置實際上是多餘的,因爲換檔並不是真的從內部調用,儘管它起初可能會出現。在這個例子中,換班真的被任何想要運行事件處理功能的人所調用,並且沒有任何行會這樣做。 – 2011-05-19 20:58:43

+0

感謝您的建議,我修改了代碼段 – 2011-05-19 22:31:43

3

有延續的關鍵是使用編碼的直接風格的能力,即使平時我將被迫代碼更難使用的方式(例如以事件驅動的風格)。

所以客戶端代碼我希望能寫會是這樣的:

reset { 
    val event1 = widget1.waitForEvent() 
    handle1(event1) 
    val event2 = widget2.waitForEvent() 
    handle2(event2) 
    val event3 = widget3.waitForEvent() 
    handle3(event3) 
} 

所以聽衆的東西就從我被隱藏。但是,當然,聽衆仍然需要在下面的某個地方。我會將它們隱藏在小部件的waitForEvent()方法中(可以添加到Widget類中,也可以通過隱式轉換獲得)。該方法看起來像:

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

這至少在概念層面。爲了實現這個功能,你需要添加一些類型和/或@cps註釋。