Haskell Language
反応性バナナ
サーチ…
外部イベントをライブラリに注入する
この例は、例えばreactive-banana-wxのような具体的なGUIツールキットには関係していません。代わりに、任意のIO
アクションをFRP機械に注入する方法を示します。
Control.Event.Handler
モジュールはaddHandler
関数を提供し、 addHandler
AddHandler a
とa -> IO ()
値のペアを作成します。前者は、 Event a
値を取得するために反応型バナナ自体によって使用され、後者は、対応するイベントをトリガするために使用されるプレーンな関数です。
import Data.Char (toUpper) import Control.Event.Handler import Reactive.Banana main = do (inputHandler, inputFire) <- newAddHandler
私たちの場合、ハンドラのa
パラメータはString
型ですが、後で記述されるコンパイラの推論を可能にするコードです。
ここでは、FRP駆動システムを記述するEventNetwork
を定義します。これはcompile
関数を使って行われます:
main = do (inputHandler, inputFire) <- newAddHandler compile $ do inputEvent <- fromAddHandler inputHandler
fromAddHandler
関数は、 AddHandler a
値をEvent a
に変換AddHandler a
ます。これについては、次の例を参照してください。
最後に、ユーザー入力に関するイベントを発生させる「イベントループ」を起動します。
main = do (inputHandler, inputFire) <- newAddHandler compile $ do ... forever $ do input <- getLine inputFire input
イベントタイプ
反応型バナナでは、 Event
タイプは時間内のいくつかのイベントのストリームを表します。 Event
は時間的に連続していないという意味でのアナログインパルス信号に似ています。その結果、 Event
はFunctor
型付きのインスタンスです。 2つのEvent
は、異なる時間に発生する可能性があるため、組み合わせることはできません。 Event
の[現在]の値で何かを行い、何らかのIO
アクションでそれに反応することができます。
Event
s値の変換は、 fmap
を使用して行います。
main = do (inputHandler, inputFire) <- newAddHandler compile $ do inputEvent <- fromAddHandler inputHandler -- turn all characters in the signal to upper case let inputEvent' = fmap (map toUpper) inputEvent
Event
への反応は同じように行われます。まずa -> IO ()
型のアクションでfmap
し、関数をreactimate
せるためにreactimate
ます:
main = do (inputHandler, inputFire) <- newAddHandler compile $ do inputEvent <- fromAddHandler inputHandler -- turn all characters in the signal to upper case let inputEvent' = fmap (map toUpper) inputEvent let inputEventReaction = fmap putStrLn inputEvent' -- this has type `Event (IO ()) reactimate inputEventReaction
inputFire "something"
が呼び出されるたびに、 "SOMETHING"
が表示されます。
行動タイプ
継続的な信号を表現するために、反応型バナナの特徴はBehavior a
型である。 Event
とは異なり、 Behavior
はApplicative
であり、n個の純粋な関数( <$>
と<*>
を使用)を使用してn個のBehavior
を組み合わせることができます。
Event a
Behavior a
からBehavior a
を取得するには、 accumE
関数がaccumE
ます。
main = do (inputHandler, inputFire) <- newAddHandler compile $ do ... inputBehavior <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent
accumE
は、 Behavior
の初期値とEvent
とり、新しい値に設定する関数を含みます。
Event
sと同様に、 fmap
を使用して現在のBehavior
の値を操作できますが、 (<*>)
と組み合わせることもできます。
main = do (inputHandler, inputFire) <- newAddHandler compile $ do ... inputBehavior <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent inputBehavior' <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent let constantTrueBehavior = (==) <$> inputBehavior <*> inputBehavior'
Behavior
変更に反応するには、 changes
機能changes
ます:
main = do (inputHandler, inputFire) <- newAddHandler compile $ do ... inputBehavior <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent inputBehavior' <- accumE "" $ fmap (\oldValue newValue -> newValue) inputEvent let constantTrueBehavior = (==) <$> inputBehavior <*> inputBehavior' inputChanged <- changes inputBehavior
注目すべき唯一のことは、 changes
がEvent a
代わりにEvent a
Event (Future a)
返すEvent a
です。このため、 reactimate'
代わりにreactimate
reactimate'
を使用する必要がありreactimate
。この背後にある根拠は、ドキュメントから入手できます。
イベントネットワークの起動
EventNetwork
イベントが有効になる前に、 compile
によって返されたEventNetwork
起動compile
必要があります。
main = do
(inputHandler, inputFire) <- newAddHandler
eventNetwork <- compile $ do
inputEvent <- fromAddHandler inputHandler
let inputEventReaction = fmap putStrLn inputEvent
reactimate inputEventReaction
inputFire "This will NOT be printed to the console!"
actuate eventNetwork
inputFire "This WILL be printed to the console!"