bitterharvest’s diary

A Bitter Harvestは小説の題名。作者は豪州のPeter Yeldham。苦闘の末に勝ちえた偏見からの解放は命との引換になったという悲しい物語

Reactive-bananaの紹介(4)

5.Counter

振舞いについては、前の二つの記事で、テキストフィールドを用いて説明した。今回は、reactive programmingのもう一つの一翼を担うイベントについて説明する。

この記事でのプログラムのタイトルは、Counterである。upとdownのボタンがあり、upが押されると一つ増やされ、downが押されると一つ減らされるcounterがあるとする。counterは押された数で変化する振舞いとなり、upとdownはイベントとなる。それでは、このプログラムを見ることにしよう。初期画面は次のようになる。
f:id:bitterharvest:20151130201621p:plain

5.1 ユーザ・インタフェース

ユーザ・インタフェースは、これまでのフレームと結果表示のテキストフィールドに付け加えて、upとdownのための二つのボタンを用意する。次のようになる。

    f       <- frame [text := "Counter"]
    bup     <- button f [text := "Up"]
    bdown   <- button f [text := "Down"]
    output  <- staticText f []
    
    set f [layout := margin 10 $
            column 5 [widget bup, widget bdown, widget output]]

なお、buttonはモジュールGraphics.UI.WXで定義されている。

5.2 イベント

ボタンはイベントとして次のように表される。

        eup   <- event0 bup   command
        edown <- event0 bdown command

上記のプログラムで、event0はモジュールReactive.Banana.WXが用意した関数で、ウィジェットとコマンドを引数にとる。このプログラムでは、ウィジェットはボタンである。コマンドのところはcommandと書く。これはウィジェットに対するコマンドと解釈される。ここでは、ボタンがウィジェットなので、コマンドはボタンを押すことである。

5.3 振舞いとイベントのAPI

upのボタンが押されたときに1増やすイベントは次のようになる(<$はfmap . constの中置関数である。これについては次の記事で詳しく説明する)。

          (+1) <$ eup

downに対しては次のようになる。

          subtract 1 <$ edown

二つのイベントは、関数unionsにより集められる。

          unions [ (+1) <$ eup, subtract 1 <$ edown]

上記を時間軸に沿って集積したものは、関数accumBを用いて、次のように記述できる。

          accumB 0 $ unions [ (+1) <$ eup, subtract 1 <$ edown]

この値が、counterのそれとなるので、次のように記述する。

        (counter :: Behavior Int)
            <- accumB 0 $ unions
                [ (+1)       <$ eup
                , subtract 1 <$ edown
                ]

unionsもaccumBもReactive.Banana.Combinatorsで用意されている。

5.4 アニメーションとして表示する

counterの値を示すには、例によって、アニメーション化すればよいので次のようにする。

        sink output [text :== show <$> counter] 

5.5 プログラム全体

プログラムはこのホームページで公表されているが、転載する。

{-----------------------------------------------------------------------------
    reactive-banana-wx
    
    Example: Counter
------------------------------------------------------------------------------}
{-# LANGUAGE ScopedTypeVariables #-}
    -- allows pattern signatures like
    -- do
    --     (b :: Behavior Int) <- stepper 0 ...

import Graphics.UI.WX hiding (Event)
import Reactive.Banana
import Reactive.Banana.WX

{-----------------------------------------------------------------------------
    Main
------------------------------------------------------------------------------}
main :: IO ()
main = start $ do
    f       <- frame [text := "Counter"]
    bup     <- button f [text := "Up"]
    bdown   <- button f [text := "Down"]
    output  <- staticText f []
    
    set f [layout := margin 10 $
            column 5 [widget bup, widget bdown, widget output]]

    let networkDescription :: MomentIO ()
        networkDescription = do
        
        eup   <- event0 bup   command
        edown <- event0 bdown command
        
        (counter :: Behavior Int)
            <- accumB 0 $ unions
                [ (+1)       <$ eup
                , subtract 1 <$ edown
                ]

        sink output [text :== show <$> counter] 

    network <- compile networkDescription    
    actuate network