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!"